Developer Reference
API Reference
The Preprints.ai REST API lets you programmatically submit preprints for assessment, retrieve A5–E1 quality grades, and integrate evidence-based scores into your platform, journal workflow, or LLM pipeline.
Base URL: https://api.preprints.ai · All requests require an API key in the X-API-Key header. Get a free key →
Free
100
lookups/day · 10 assessments/day
Pro — $99/mo
10K
lookups/day · 500 assessments/day
Publisher — $499/mo
∞
unlimited lookups · 5K assessments/day · SLA
Authentication
Pass your API key as the X-API-Key request header. Keys are available in your dashboard .
curl
Python
JavaScript
# Include your key in every request
curl https://api.preprints.ai/v2/score/10.1101/2025.01.15.633214 \
-H "X-API-Key: prai_live_xxxxxxxxxxxxxxxxxxxx"
import requests
headers = {
"X-API-Key" : "prai_live_xxxxxxxxxxxxxxxxxxxx"
}
response = requests.get (
"https://api.preprints.ai/v2/score/10.1101/2025.01.15.633214" ,
headers=headers
)
data = response.json ()
const response = await fetch (
"https://api.preprints.ai/v2/score/10.1101/2025.01.15.633214" ,
{
headers: {
"X-API-Key" : "prai_live_xxxxxxxxxxxxxxxxxxxx"
}
}
);
const data = await response.json ();
Try It Live
Paste any DOI, arXiv ID, or bioRxiv URL to fetch a cached assessment directly from the API.
GET /v2/score/{doi}
Fetch a cached assessment — no API key required for the demo
Run Request
Submit a paper for assessment. Returns a job_id to poll with GET /v2/status/{job_id}. Processing typically takes 45–120 seconds.
Cached results: If the paper has been previously assessed, this endpoint returns the cached result immediately with status: "complete".
Parameter Type Description
doioptional string DOI of the paper (e.g. 10.1101/2025.01.15.633214)
arxiv_idoptional string arXiv ID (e.g. 2501.12345)
pdf_urloptional string Direct URL to the PDF. Required if DOI/arXiv not resolvable.
callback_urloptional string Webhook URL to POST result to on completion. Publisher
priorityoptional integer Processing priority 1–10. Default: 5. Pro+
curl
Python
JavaScript
curl -X POST https://api.preprints.ai/v2/assess \
-H "X-API-Key: prai_live_xxxx" \
-H "Content-Type: application/json" \
-d '{"doi": "10.1101/2025.01.15.633214"}'
response = requests.post (
"https://api.preprints.ai/v2/assess" ,
headers=headers,
json={"doi" : "10.1101/2025.01.15.633214" }
)
job = response.json ()
job_id = job["job_id" ]
const job = await fetch ("https://api.preprints.ai/v2/assess" , {
method: "POST" ,
headers: { "X-API-Key" : "prai_live_xxxx" , "Content-Type" : "application/json" },
body: JSON .stringify ({ doi: "10.1101/2025.01.15.633214" })
}).then (r => r.json ());
const jobId = job.job_id;
Response — 202 Accepted
{
"job_id" : "job_01JKXXXXXXXXXXXXXXXX" ,
"status" : "queued" ,
"doi" : "10.1101/2025.01.15.633214" ,
"estimated_seconds" : 90 ,
"poll_url" : "https://api.preprints.ai/v2/status/job_01JKXXX"
}
Retrieve a completed assessment by DOI. Returns 404 if the paper has not yet been assessed.
curl
Python
JavaScript
curl https://api.preprints.ai/v2/score/10.1101/2025.01.15.633214 \
-H "X-API-Key: prai_live_xxxx"
doi = "10.1101/2025.01.15.633214"
result = requests.get (
f"https://api.preprints.ai/v2/score/{doi}" ,
headers=headers
).json ()
const doi = "10.1101/2025.01.15.633214" ;
const result = await fetch (
`https://api.preprints.ai/v2/score/${doi}` ,
{ headers: { "X-API-Key" : "prai_live_xxxx" } }
).then (r => r.json ());
Response — 200 OK
{
"doi" : "10.1101/2025.01.15.633214" ,
"grade" : "B4" ,
"integrity" : {
"grade" : "B" ,
"label" : "Convincing" ,
"score" : 0.82 ,
"modules" : {
"paper_mill" : { "status" : "pass" , "score" : 0.97 },
"statistical" : { "status" : "pass" , "verified" : 12 , "errors" : 0 },
"open_data" : { "status" : "partial" , "repos" : ["GEO:GSE12345" ] },
"image_integrity" :{ "status" : "pass" }
}
},
"novelty" : {
"grade" : "4" , "label" : "Fundamental" , "score" : 0.78
},
"confidence" : 0.85 ,
"panel_agreement" :0.91 ,
"agents" : [
{ "role" : "Methodologist" , "model" : "claude-sonnet-4-20250514" , "score" : 7.8 },
{ "role" : "Statistician" , "model" : "gpt-4o-2024-11-20" , "score" : 8.1 },
/* ... 7 more agents */
],
"assessed_at" : "2026-02-25T10:30:00Z" ,
"version" : "2.0"
}
Bulk lookup — fetch assessments for up to 100 DOIs in a single request. Pass DOIs as a comma-separated query parameter.
Parameter Type Description
doisrequired string Comma-separated list of up to 100 DOIs
include_evidenceoptional boolean Include full evidence object per paper. Default: false.
curl
Python
curl "https://api.preprints.ai/v2/scores?dois=10.1101/2025.01.15.633214,10.1101/2025.02.01.987654" \
-H "X-API-Key: prai_live_xxxx"
dois = ["10.1101/2025.01.15.633214" , "10.1101/2025.02.01.987654" ]
response = requests.get (
"https://api.preprints.ai/v2/scores" ,
params={"dois" : "," .join (dois)},
headers=headers
).json ()
Poll the status of a submitted assessment job. Poll every 10–30 seconds until status: "complete".
Response — 200 OK
{
"job_id" : "job_01JKXXXXXXXXXXXXXXXX" ,
"status" : "complete" , // queued | processing | complete | failed
"progress" : 100 , // 0-100
"stage" : "deliberation" ,// layer1 | agentic | deliberation
"result_url" : "https://api.preprints.ai/v2/score/10.1101/..."
}
Error Codes
Status Code Description
400 invalid_identifier DOI or arXiv ID could not be resolved
401 missing_api_key No API key provided
403 invalid_api_key API key is invalid or revoked
404 not_assessed Paper exists but has not been assessed. Use POST /v2/assess.
429 rate_limited Daily quota exceeded. Retry-After header included.
503 assessment_unavailable PDF inaccessible or insufficient text extracted
Score Badges
Embed a dynamically generated SVG score badge on your paper pages. Colour-coded by grade.
HTML
Markdown
<!-- Embed badge for a specific DOI -->
<img
src="https://preprints.ai/badge/10.1101/2025.01.15.633214"
alt="Preprints.ai score: B4"
/>
[](https://preprints.ai/doi/10.1101/2025.01.15.633214)
See the embed guide for badge customisation options and live previews.
Webhooks Publisher
Receive assessment results via webhook instead of polling. Configure callback URLs in your dashboard .
Signature verification: Every webhook includes an X-Preprints-Signature header — an HMAC-SHA256 of the request body using your webhook secret. Always verify this before processing.
Python
Node.js
import hmac, hashlib
def verify_webhook (payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new (
secret.encode (), payload, hashlib.sha256
).hexdigest ()
return hmac.compare_digest (expected, signature)
const crypto = require ('crypto' );
function verifyWebhook (payload, signature, secret) {
const expected = crypto
.createHmac ('sha256' , secret)
.update (payload)
.digest ('hex' );
return crypto.timingSafeEqual (
Buffer .from (expected),
Buffer .from (signature)
);
}