Use the key below to get started immediately — no sign-up required.
The limit resets at midnight UTC. If you need higher volume or synthesis analysis, get in touch.
Set it as an environment variable, then try a text search:
# Set once
export API_KEY="pw_live_65d65b6a1f42920fda777e306f0005c0b9141bba4327dff203be010c1f34be72"
# Text search — returns the 10 most relevant papers
curl -X POST https://priorwork.fyi/api/query \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "contrastive learning for vision transformers",
"max_results": 10
}'
https://priorwork.fyi. Pass your key in the
X-API-Key header on every request.
Find papers similar to text or to a paper you are writing. Both endpoints take an API key and count against your daily quota.
Search by free-text query. Optionally request per-paper summaries.
curl -X POST https://priorwork.fyi/api/query \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "diffusion models for image generation",
"analysis": "summaries",
"max_results": 10
}'
| Field | Type | Default | Description |
|---|---|---|---|
query |
string | required | Free-text query |
analysis |
string | "none" |
"none" or "summaries" — see below |
max_results |
int | 10 |
1–10 |
Search using the sections of a paper (title, abstract, intro, …). Produces the best results because the embedding is built from your full paper structure.
curl -X POST https://priorwork.fyi/api/search/paper-sections \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sections": {
"title": "Your Paper Title",
"Abstract": "We present a novel approach to ...",
"1. Introduction": "This problem is important because ...",
"References": "[1] Smith et al. 2023. ..."
},
"analysis": "summaries",
"check_citations": true,
"max_results": 10
}'
| Field | Type | Default | Description |
|---|---|---|---|
sections |
object | required | Section name → text. The first 4 sections are used for the search embedding. |
analysis |
string | "none" |
"none" or "summaries" |
check_citations |
bool | false |
Marks results that already appear in your References section via is_cited |
max_results |
int | 10 |
1–10 |
Give it a public PDF URL — an arXiv link, say. The PDF is downloaded, its text extracted, and similar papers returned. Same response shape as the other two.
curl -X POST https://priorwork.fyi/api/search/pdf-url \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://arxiv.org/pdf/2301.00001", "max_results": 10}'
| Field | Type | Default | Description |
|---|---|---|---|
url | string | required | Public http or https link to a PDF |
max_results | int | 10 |
1–10 |
Slower than the text endpoints — it has to fetch and parse the file before searching.
The paper indexes and how many papers each holds. Useful for showing corpus size, or checking the index grew after a proceedings drop.
curl https://priorwork.fyi/api/indexes -H "X-API-Key: $API_KEY"
// → {"indexes": [{"name": "gte", "paper_count": 128730, "provider": "unknown"}]}
analysis Parameter| Value | Description | Extra latency |
|---|---|---|
"none" |
Search results only, no LLM processing | — |
"summaries" |
Per-paper structured notes: problem, method, key results, evidence strength. Uses cached extractions when available. | +5–15 s |
For the topics endpoints, see the response tables in the Topics API section above.
analysis: "none"){
"query": "diffusion models for image generation",
"total_results": 10,
"uncited_count": 8,
"cited_count": 2,
"results": [
{
"paper_id": "DDPM_2020",
"title": "Denoising Diffusion Probabilistic Models",
"authors": "Ho, Jain, Abbeel",
"year": "2020",
"conference": "NeurIPS",
"abstract": "We present ...",
"similarity": 0.91,
"is_cited": false,
"download_url": "https://..."
}
],
"llm_analysis": null,
"llm_error": null
}
analysis: "summaries")The results array is unchanged. An llm_analysis.extractions list is added:
{
"results": [ /* same as above */ ],
"llm_analysis": {
"extractions": [
{
"paper_id": "DDPM_2020",
"problem": "The core problem addressed",
"core_insight": "The key observation that justifies the approach",
"method_summary": "How the paper solves it",
"key_results": ["Result 1", "Result 2"],
"baselines_beaten": ["Baseline A"],
"limitations": ["Main limitation", "Secondary limitation"],
"applicability_conditions":"Assumptions and constraints on when this applies",
"positioning": "Gap in prior work the authors claim to fill",
"evidence_strength": "4/5 — Strong empirical evidence with ablations"
}
],
"error": null
}
}
is_cited is only meaningful when check_citations: true is passed.
similarity values above 0.83 indicate a strong match; above 0.97 likely means the paper is already in your references.
A different corpus and a different question. Paper search returns papers from a 128,730-paper index; these two work on the research topics our problem discovery pipeline publishes — ranking them against an interest, or judging whether a project you describe has already been done.
The two are not alike on auth. Both need a key;
search accepts any valid one, including the public free key,
while validate requires a key with analysis rights.
Every published topic, in one document. No key, no parameters, no ranking —
this is the corpus, and it is useful on its own if you want to browse rather
than search. It is also what you join /api/topics/search results
against, since that endpoint returns keys rather than text.
curl https://priorwork.fyi/api/research-suggestions
// → {"artifact": "162e7c8fb8cd", "generated": "2026-09-08", "topics": [ … ]}
artifact is the id of this exact topic set. It changes whenever
the published set changes — a new topic, a withdrawn one, or the same topics
with rewritten text. /api/topics/search returns the same id, so a
client that caches this document can compare the two and refetch only when it
moves:
hits = requests.post(f"{BASE}/api/topics/search", json={"q": q}).json()
if hits["artifact"] != cached_artifact_id:
blob = requests.get(f"{BASE}/api/research-suggestions").json()
cached_topics, cached_artifact_id = blob["topics"], blob["artifact"]
Do not infer this from a key that fails to resolve. A
key derives from a topic's supporting papers, so a run that
rewrites every title leaves every key untouched — a cache waiting on a
resolution failure would serve that stale indefinitely.
About 4.6 MB for ~180 topics, because each one
carries its full reasoning. Fetch it once and keep it rather than calling it
per search — the topics change only when the pipeline runs, and
generated tells you when that was.
| Field | Type | What it is |
|---|---|---|
key | string | Stable id, derived from the papers behind the topic. This is what
/api/topics/search returns |
title | string | The project, phrased as a paper title |
why | string | The limitation several papers independently hit |
impact | string | What changes if it works |
risk | string | The reason it might not |
approaches | string | Concrete ways in, numbered |
region | string | The research area it was mined from |
papers | int | How many papers the evidence rests on |
statements | array | The verbatim limitations those papers stated |
sources, neighbours | array | The papers themselves, with ids you can look up |
verification.verdict | string | OPEN if a prior-art check found nothing that closes it |
children | array | Sub-problems, on the ~20% of topics that split |
hyde | string | The synthetic abstract the ranker matches your query against |
title, why, impact, risk
and approaches are the ones worth reading. The rest is
provenance, kept so any claim can be traced back to the papers behind it.
These are generated, not curated.
verification.verdict records a prior-art check, but that check
finds only about a third of work that already exists — an OPEN
verdict is not evidence the problem is unsolved. Treat a topic as a lead to
investigate.
Each topic is stored as a synthetic abstract of the paper that would close it, so the match is intent against intent. Write what you want to work on the way you would write it in an abstract. Measured against the live index:
| Style | Query | Results | Top score |
|---|---|---|---|
| keywords | efficient transformers edge devices |
0 | — |
| a title | Efficient Vision Transformers for Edge Deployment |
21 | 0.385 |
| a question | how can I make vision transformers efficient on edge devices? |
18 | 0.368 |
| prose | We want to cut the compute and memory cost of vision transformers so they can run on resource-constrained edge hardware without losing accuracy. |
29 | 0.430 |
Keywords return nothing. That is not a bug — a bare term has
almost no overlap with a full abstract, so it lands under the relevance floor.
The same is true of a bare method name: diffusion models returns
0 results, while "generated images are hard to attribute
to their source model" returns 26. Describe the problem,
not the technique.
Broad is worse, not better. machine learning
returns 5 and evaluation returns 2, while
"detecting hallucination in retrieval-augmented generation" scores
0.556 — the highest in any test here. Specificity helps; vagueness has nothing
to match against.
More detail helps, up to about a thousand characters.
| Query length | Results | Top score |
|---|---|---|
| 159 chars — one sentence | 59 | 0.462 |
| 459 chars — a paragraph | 85 | 0.598 |
| 1,059 chars | 96 | 0.652 |
| 1,953 chars | 98 | 0.652 (unchanged) |
The ranker truncates at 256 tokens, roughly 1,000 characters, so anything past that is ignored — the last two rows score identically. Put the substance first; there is no benefit to sending a whole paper.
Ranks the published research topics against a free-text description of what
you want to work on. Separate from paper search: it never touches the paper
index, answers in about 5 ms, and returns topic keys you join against
GET /api/research-suggestions.
Needs a key. Any valid key will do — the public free key above works, and no analysis rights are required.
It has its own allowance of 2,000 requests per day, counted separately from paper search in both directions: ranking topics never consumes your paper-search quota, and a key that has exhausted that quota still works here.
The site's own pages call this without a key and are not
capped — a page cannot hold a secret, so a first-party request is attributed
to the site's key instead. The exemption is decided by Origin,
so it does not extend to an API caller who omits the header.
curl -X POST https://priorwork.fyi/api/topics/search \
-H "Content-Type: application/json" \
-d '{"q": "protein folding"}'
# → {"degraded": false, "results": [{"key": "6e3242e12882", "score": 0.45}, …]}
| Field | Type | Default | Description |
|---|---|---|---|
q | string | required | What you want to work on, in your own words |
min_score | float | 0.30 |
Relevance floor. Below it a “match” is vocabulary collision rather than subject overlap |
limit | int | unset | A safety bound, not a page size. Leave it off and the floor decides how many results there are; clamps at 500 |
Response
{
"artifact": "162e7c8fb8cd",
"degraded": false,
"results": [
{ "key": "6e3242e12882", "score": 0.450090 },
{ "key": "a9d4b3708af5", "score": 0.413924 }
]
}
| Field | Type | Meaning |
|---|---|---|
artifact | string | Id of the topic set these keys came from. Compare it against a cached copy to know when to refetch |
results[].key | string | Stable topic id. Join it against GET /api/research-suggestions
— this endpoint returns keys, never topic text, so the two can never
disagree about what a topic says |
results[].score | float | Cosine similarity, descending. Not comparable across queries — rank within a response, don't threshold between them |
degraded | bool | true means ranking was unavailable and
results is empty. Fall back to whatever you had |
stale | bool | Present only on a deployment mismatch: the ranker's vectors describe a different set of topics, so its keys would not resolve |
Zero results is a legitimate answer, not an error — it means
nothing published is close to that question, which is more useful than a list
of near-misses. degraded: true is the different case: ranking was
unavailable, so fall back to whatever you had. Limited to 60 requests per minute
per address and 2,000 per day per key.
Retrieves the closest prior work for a project description and asks whether any of it already does the job. Requires an API key with analysis rights. Each call counts against your daily quota.
curl -X POST https://priorwork.fyi/api/topics/validate \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Verifier-free reward shaping for open-domain reasoning",
"abstract": "We remove the dependence on rule-based answer verifiers by ...",
"why_it_matters": "Current methods only work where answers can be checked ..."
}'
| Field | Type | Default | Description |
|---|---|---|---|
title | string | required | Working title. Worth about 30 points of top-1 retrieval accuracy — do not leave it out |
abstract | string | required | The idea, as you would write it in an abstract |
why_it_matters | string | required | The limitation this removes. This is the framing the judge matches on |
max_papers | int | 10 |
3–20. How many retrieved papers the judge reads |
Response
{
"verdict": "PARTIAL",
"found_prior_work": true,
"reason": "Paper [2] removes the verifier requirement, but only for maths.",
"papers": [
{ "paper_id": "Reinforcing_General_Reasoning_Without_Verifiers_2026",
"title": "Reinforcing General Reasoning Without Verifiers",
"similarity": 0.824, "year": "2026",
"conference": "ICLR", "download_url": "https://..." }
],
"considered": [ /* every paper the judge read, same shape */ ],
"sensitivity_note": "This detects roughly a third of work that already exists ..."
}
| Field | Type | Meaning |
|---|---|---|
verdict | string | ALREADY_DONE, PARTIAL or OPEN.
A 200 always carries one of these three |
found_prior_work | bool | True for ALREADY_DONE and PARTIAL only.
The single field worth branching on |
reason | string | One sentence naming the paper that decided it |
papers | array | Only the papers the verdict rests on. Empty for OPEN |
considered | array | Everything the judge read, so you can see what it was working from. Anything at or above 0.97 similarity — your own paper — is excluded before judging |
sensitivity_note | string | Shipped in every response, because the caveat below decides whether the answer is useful or misleading |
title is not decoration. The index stores every paper as
Title: …\nAbstract: …, so supplying one makes your submission the
same shape as the documents it is matched against — worth about
30 points of top-1 accuracy (63% → 95% over 100 queries whose
target paper was known).
why_it_matters should name the constraint you are
removing, not the benefit. Gaps in this corpus are overwhelmingly
phrased as limitations — "requires rule-based verifiers", "restricted to
portrait scenes", "assumes access to the retain set" — and the paper that
closes one is the paper that lifts that requirement. Matching works best when
your framing has the same shape.
// weak — states a benefit
"why_it_matters": "This would make reasoning models more useful."
// strong — names the constraint being lifted
"why_it_matters": "Current methods need a rule-based checker, which limits them to maths and code."
abstract is the work itself: what you would do and how. Minimum 50
characters, and the same thousand-character guidance as search applies — say the
substance early.
Read the verdict in one direction only. This finds roughly a
third of work that already exists, against a ~4% false-positive rate.
ALREADY_DONE and PARTIAL are worth acting on, and
papers names what decided it. OPEN means nothing was
found among the papers read — it is not evidence that your idea is
novel, which is why there is no is_novel field.
| Status | Code | When |
|---|---|---|
400 | MISSING_FIELDS |
A required field is missing or blank |
401 | AUTH_REQUIRED |
No API key supplied |
403 | ANALYSIS_NOT_PERMITTED |
Valid key, but it lacks analysis rights — the free tier |
422 | NOTHING_TO_VERIFY |
Every retrieved paper was your own submission, or nothing cleared the threshold |
503 | VERIFICATION_UNAVAILABLE |
The model was unreachable or returned no verdict. Retryable |
A 200 always carries a real verdict — the ways a judgement can
fail to exist are errors with their own codes, never a fourth verdict value.
A PDF you send — uploaded to /api/query, or fetched by us
from a URL you pass to /api/search/pdf-url — is written to
a temporary file, read, and deleted when the request finishes. Neither the
file nor its text is ever stored.
What you send through sections, and the two synthesis fields
written about it (relevance_to_query,
query_positioning), are removed after 30 days.
They are kept that long so a change to the analysis prompt can be judged
against earlier output.
analysis=summaries or synthesis the retrieved
papers go there; with analyze_my_draft your sections go too.
Leave analysis off and nothing you send leaves our hardware.