v1.0 Documentation
Integrate AI-powered research verification into your applications with our comprehensive REST API
https://www.verifyscience.co.uk
All API requests require authentication using your API key in the Authorization header as a Bearer token.
Authorization: Bearer vs_live_your_api_key_here
Choose the plan that fits your needs. All plans include both web dashboard and API access with shared verification quotas.
| Tier | Price | Verifications/Month | Rate Limit | Batch API |
|---|---|---|---|---|
| Free | ยฃ0 | 3 | 5 requests/minute | โ |
| Starter | ยฃ4.99/month | 199 | 20 requests/minute | โ |
| Professional | ยฃ9.99/month | 499 | 60 requests/minute | โ up to 20 papers |
| Enterprise | ยฃ19.99/month | 1,999 | 120 requests/minute | โ up to 50 papers |
429 Too Many Requests response.
GET /v1/me
Retrieve information about your account, including subscription tier and remaining verifications.
{
"user_id": "user_abc123",
"email": "[email protected]",
"subscription": "professional",
"verifications_left": 485,
"verifications_limit": 499,
"rate_limit": "60 requests/minute"
}
POST /v1/verifications
Submit a research paper for AI-powered methodological analysis. Returns a job ID immediately; poll for results.
{
"content": "https://journals.plos.org/plosmedicine/article?id=10.1371/journal.pmed.0020124",
"method": "url"
}
| Field | Type | Description |
|---|---|---|
| content | string ยท required | The paper to verify. URL, DOI URL, or raw text. Minimum 50 characters. For DOI links, use the full resolved article URL (e.g. journals.plos.org/...) rather than a bare doi.org/ link โ short DOIs will be rejected. |
| method | string ยท optional | "url" (default) or "text". Use "url" when content is a URL; "text" when pasting raw paper content. |
DOI links: Use the full article page URL, not the short doi.org redirect. Open-access papers on PLOS, PubMed Central, arXiv, and bioRxiv work reliably. Paywalled papers should be submitted as text โ paste the abstract and methods section directly.
{
"id": "job_dc2f5b45-73ac-420e-9eac-a78c2f9a0abe",
"status": "processing",
"message": "Verification started successfully"
}
GET /v1/verifications/{id}
Retrieve the results of a verification job by its ID.
{
"id": "job_dc2f5b45-73ac-420e-9eac-a78c2f9a0abe",
"status": "processing",
"progress": 45
}
{
"id": "job_dc2f5b45-73ac-420e-9eac-a78c2f9a0abe",
"status": "completed",
"result": {
"confidence": 78,
"confidence_level": "High",
"summary": "This research demonstrates strong methodology...",
"domain_category": "Clinical trial methodology",
"domain_detail": "Randomised controlled trial",
"criteria": [
{
"name": "Analytical Dimension",
"score": 85,
"assessment": "Adequate sample size for statistical power"
},
{
"name": "Methodology",
"score": 82,
"assessment": "Well-designed experimental approach"
}
],
"strengths": ["Large sample size", "Controlled variables"],
"limitations": ["Limited follow-up period"],
"created_at": "2026-03-25T00:30:00Z"
}
}
confidence field: A holistic assessment of the paper's credibility (0–100), constrained to within ±20 points of the mean of the per-criterion analysis scores. It reflects the paper's methodological rigour, publication context, post-publication record, and weight of subsequent evidence. A server-side guardrail enforces this constraint; if the AI-generated score deviates beyond the ±20 band, it is clamped and the original value is preserved in confidence_original.
domain_category field: Abstracted research domain detected from the paper โ e.g. "Clinical trial methodology", "Qualitative research", "Computational / engineering research". Returned for all tiers. Displayed in the UI for Professional and Enterprise subscribers only.
domain_detail field: Specific methodology type โ e.g. "Randomised controlled trial", "Phenomenological interview study". Returned for Professional and Enterprise tiers only.
framework_applied field: The primary quality assessment framework applied. Returned for Enterprise tier only under contract. Not present in this example response.
criteria_applicability field: Per-criterion applicability map โ values are "standard", "adapted", or "not applicable". Returned for Professional and Enterprise tiers only. Not present in this example response.
GET /v1/verifications
Retrieve a list of all your verification jobs with pagination support.
limit (optional) - Number of results per page (default: 50, max: 100)offset (optional) - Number of results to skip (default: 0){
"verifications": [
{
"id": "job_dc2f5b45-73ac-420e-9eac-a78c2f9a0abe",
"status": "completed",
"confidence": 78,
"created_at": "2026-03-25T00:30:00Z"
},
{
"id": "job_e4a1b2c3-d4e5-f6g7-h8i9-j0k1l2m3n4o5",
"status": "processing",
"progress": 45,
"created_at": "2026-03-24T22:15:00Z"
}
],
"total": 14,
"limit": 50,
"offset": 0
}
The API uses standard HTTP status codes to indicate success or failure.
Request succeeded
Invalid request parameters or missing required fields
Missing or invalid API key
API key valid but lacks permission for this resource
Requested resource doesn't exist
Rate limit exceeded - wait before retrying
Something went wrong on our end - please try again
{
"error": "Invalid API key",
"message": "The provided API key is invalid or has been revoked",
"code": "invalid_api_key"
}
POST /v1/compare
Submit two papers for side-by-side methodological comparison. Returns two job IDs immediately — poll each independently using GET /v1/verifications/{id}. Consumes 2 verifications. Requires Professional or Enterprise plan.
{
"content_a": "https://journals.plos.org/plosmedicine/article?id=10.1371/journal.pmed.0020124",
"content_b": "https://journals.plos.org/plosmedicine/article?id=10.1371/journal.pmed.1001743"
}
| Field | Type | Description |
|---|---|---|
| content_a | string · required | First paper. URL or raw text. Minimum 50 characters. Use full article page URL for DOIs. |
| content_b | string · required | Second paper. URL or raw text. Minimum 50 characters. Use full article page URL for DOIs. |
{
"status": "ok",
"job_a": "job_dc2f5b45-73ac-420e-9eac-a78c2f9a0abe",
"job_b": "job_f1a3c921-11bb-4c2d-b711-889900aabbcc",
"poll_a": "/v1/verifications/job_dc2f5b45-73ac-420e-9eac-a78c2f9a0abe",
"poll_b": "/v1/verifications/job_f1a3c921-11bb-4c2d-b711-889900aabbcc",
"verifications_consumed": 2,
"message": "Both papers queued for analysis. Poll each job independently using the poll URLs."
}
Polling: Both jobs run in parallel. Poll poll_a and poll_b independently every 9–15 seconds. Professional tier takes 2–4 minutes per paper.
import requests, time
API_KEY = 'vs_live_your_api_key_here'
BASE = 'https://www.verifyscience.co.uk'
H = {'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json'}
r = requests.post(f'{BASE}/v1/compare', headers=H, json={
'content_a': 'https://journals.plos.org/plosmedicine/article?id=10.1371/journal.pmed.0020124',
'content_b': 'https://journals.plos.org/plosmedicine/article?id=10.1371/journal.pmed.1001743'
})
data = r.json()
job_a, job_b = data['job_a'], data['job_b']
def poll(job_id):
for _ in range(80):
d = requests.get(f'{BASE}/v1/verifications/{job_id}', headers=H).json()
if d.get('status') == 'completed': return d['result']
if d.get('status') == 'failed': raise Exception(d.get('error'))
time.sleep(9)
raise TimeoutError('Job timed out')
result_a, result_b = poll(job_a), poll(job_b)
delta = result_a['confidence'] - result_b['confidence']
print(f"A: {result_a['confidence']} B: {result_b['confidence']} Delta: {delta:+d}")
POST /api/verify-batch
Submit a list of paper URLs for parallel verification. Processing runs in the background โ results are delivered to your registered email address when complete. The response returns a batch_id for status polling.
{
"csv_content": "https://pubmed.ncbi.nlm.nih.gov/123456/\nhttps://www.nature.com/articles/example\nhttps://doi.org/10.1038/s41586-026-00001-1",
"method": "url"
}
csv_content
required ยท string
One URL or DOI per line. Blank lines are ignored. A header row starting with url, doi, or title is automatically detected and skipped.
method
optional ยท string ยท default: "url"
Input type. Accepted values: "url", "text".
{
"batch_id": "batch_550e8400-e29b-41d4-a716-446655440000",
"total_papers": 3,
"status": "queued",
"message": "Batch verification queued successfully",
"poll_url": "/api/verify-batch-status?batch_id=batch_550e8400-e29b-41d4-a716-446655440000",
"estimated_time_minutes": 2
}
{
"status": "error",
"error": {
"code": "BATCH_TOO_LARGE",
"message": "Batch exceeds limit for professional tier",
"details": {
"papers_submitted": 25,
"max_allowed": 20,
"tier": "professional"
},
"next_steps": [
"Reduce to 20 papers or less",
"Enterprise plan supports up to 50 papers per batch"
]
}
}
GET /api/verify-batch-status?batch_id={batch_id}
Poll for real-time batch processing progress. The recommended polling interval is 5 seconds. When should_poll is false, processing is complete and polling should stop.
batch_id
required ยท string
The batch ID returned from the submit endpoint. Format: batch_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
include_details
optional ยท boolean ยท default: false
When true, includes full verification results for all completed papers in the response.
{
"batch_id": "batch_550e8400-e29b-41d4-a716-446655440000",
"status": "processing",
"total_papers": 3,
"completed_papers": 1,
"failed_papers": 0,
"progress_percent": 33,
"success_rate": 100,
"created_at": "2026-04-05T08:30:00Z",
"started_at": "2026-04-05T08:30:05Z",
"estimated_seconds_remaining": 120,
"should_poll": true,
"poll_interval_seconds": 5
}
{
"batch_id": "batch_550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"total_papers": 3,
"completed_papers": 3,
"failed_papers": 0,
"progress_percent": 100,
"success_rate": 100,
"created_at": "2026-04-05T08:30:00Z",
"started_at": "2026-04-05T08:30:05Z",
"completed_at": "2026-04-05T08:32:18Z",
"duration_seconds": 133,
"should_poll": false,
"verifications": [
{
"id": "job_dc2f5b45-73ac-420e-9eac-a78c2f9a0abe",
"batch_index": 0,
"status": "completed",
"result": {
"title": "Effects of Exercise on Cognitive Function in Older Adults",
"confidence": 82,
"summary": "Well-designed RCT with adequate statistical power...",
"analysis": [
{ "criterion": "Analytical Dimension", "score": 85, "explanation": "..." },
{ "criterion": "Assessment Area", "score": 80, "explanation": "..." }
]
},
"completed_at": "2026-04-05T08:31:02Z"
}
]
}
| Plan | Batch API Access | Max papers per batch | Processing | Results delivery |
|---|---|---|---|---|
| Free | โ | โ | โ | โ |
| Starter | โ | โ | โ | โ |
| Professional | โ | 20 | Parallel ยท 5 concurrent | Email + in-app |
| Enterprise | โ | 50 | Parallel ยท 5 concurrent | Email + in-app |
Batch jobs consume your monthly verification quota. A 20-paper batch deducts 20 verifications from your allowance. The background processor runs every 60 seconds and applies a 30-minute timeout to any stalled job.
// Start a verification
const startVerification = async () => {
const response = await fetch('https://www.verifyscience.co.uk/v1/verifications', {
method: 'POST',
headers: {
'Authorization': 'Bearer vs_live_your_api_key_here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
text: 'Your research text here...',
metadata: {
title: 'Study Title',
source: 'Journal Name'
}
})
});
const data = await response.json();
console.log('Verification started:', data.id);
// Poll for results
return pollForResults(data.id);
};
// Check verification status
const pollForResults = async (jobId) => {
const response = await fetch(`https://www.verifyscience.co.uk/v1/verifications/${jobId}`, {
headers: {
'Authorization': 'Bearer vs_live_your_api_key_here'
}
});
const data = await response.json();
if (data.status === 'completed') {
console.log('Verification complete!', data.result);
return data.result;
} else {
console.log('Still processing...', data.progress, '%');
// Wait 2 seconds and check again
await new Promise(resolve => setTimeout(resolve, 2000));
return pollForResults(jobId);
}
};
startVerification();
import requests
import time
API_KEY = 'vs_live_your_api_key_here'
BASE_URL = 'https://www.verifyscience.co.uk'
headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
}
# Start verification
response = requests.post(
f'{BASE_URL}/v1/verifications',
headers=headers,
json={
'text': 'Your research text here...',
'metadata': {
'title': 'Study Title',
'source': 'Journal Name'
}
}
)
data = response.json()
job_id = data['id']
print(f'Verification started: {id}')
# Poll for results
while True:
response = requests.get(
f'{BASE_URL}/v1/verifications/{id}',
headers=headers
)
data = response.json()
if data['status'] == 'completed':
print('Verification complete!')
print(f"Confidence: {data['result']['confidence']}%")
print(f"Summary: {data['result']['summary']}")
break
else:
print(f"Still processing... {data.get('progress', 0)}%")
time.sleep(2)
const axios = require('axios');
const API_KEY = 'vs_live_your_api_key_here';
const BASE_URL = 'https://www.verifyscience.co.uk';
const client = axios.create({
baseURL: BASE_URL,
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
}
});
async function verifyResearch(text, metadata = {}) {
try {
// Start verification
const { data: startData } = await client.post('/v1/verifications', {
text,
metadata
});
console.log('Verification started:', startData.id);
// Poll for results
while (true) {
const { data: statusData } = await client.get(
`/v1/verifications/${startData.id}`
);
if (statusData.status === 'completed') {
console.log('Verification complete!');
return statusData.result;
}
console.log(`Progress: ${statusData.progress}%`);
await new Promise(resolve => setTimeout(resolve, 2000));
}
} catch (error) {
console.error('Error:', error.response?.data || error.message);
throw error;
}
}
// Example usage
verifyResearch(
'Your research text here...',
{ title: 'Study Title', source: 'Journal Name' }
).then(result => {
console.log('Confidence:', result.confidence);
console.log('Summary:', result.summary);
});
import requests
import time
API_KEY = 'vs_live_your_api_key_here'
BASE_URL = 'https://www.verifyscience.co.uk'
headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
}
# Submit a batch of paper URLs (Professional: max 20, Enterprise: max 50)
urls = [
'https://pubmed.ncbi.nlm.nih.gov/123456/',
'https://www.nature.com/articles/example',
'https://doi.org/10.1038/s41586-026-00001-1'
]
response = requests.post(
f'{BASE_URL}/api/verify-batch',
headers=headers,
json={
'csv_content': '\n'.join(urls),
'method': 'url'
}
)
data = response.json()
batch_id = data['batch_id']
print(f'Batch submitted: {batch_id}')
print(f'Estimated time: {data["estimated_time_minutes"]} minutes')
# Poll for results every 5 seconds
while True:
response = requests.get(
f'{BASE_URL}/api/verify-batch-status',
headers=headers,
params={
'batch_id': batch_id,
'include_details': 'true'
}
)
status = response.json()
print(f'Progress: {status["progress_percent"]}% '
f'({status["completed_papers"]}/{status["total_papers"]} complete)')
if not status.get('should_poll', True):
print('\nBatch complete!')
for v in status.get('verifications', []):
if v['status'] == 'completed':
title = v['result'].get('title', 'Untitled')
confidence = v['result'].get('confidence', 0)
print(f' [{confidence}%] {title}')
else:
print(f' [FAILED] Paper {v["batch_index"] + 1}: {v.get("error")}')
break
time.sleep(status.get('poll_interval_seconds', 5))
const API_KEY = 'vs_live_your_api_key_here';
const BASE_URL = 'https://www.verifyscience.co.uk';
const headers = {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
};
// Submit batch โ Professional: max 20 URLs, Enterprise: max 50
const urls = [
'https://pubmed.ncbi.nlm.nih.gov/123456/',
'https://www.nature.com/articles/example',
'https://doi.org/10.1038/s41586-026-00001-1'
];
const submitBatch = async () => {
const res = await fetch(`${BASE_URL}/api/verify-batch`, {
method: 'POST',
headers,
body: JSON.stringify({
csv_content: urls.join('\n'),
method: 'url'
})
});
const { batch_id, estimated_time_minutes } = await res.json();
console.log(`Batch submitted: ${batch_id}`);
console.log(`Estimated time: ${estimated_time_minutes} minutes`);
// Poll until complete
while (true) {
const statusRes = await fetch(
`${BASE_URL}/api/verify-batch-status?batch_id=${batch_id}&include_details=true`,
{ headers }
);
const status = await statusRes.json();
console.log(`Progress: ${status.progress_percent}% ` +
`(${status.completed_papers}/${status.total_papers})`);
if (!status.should_poll) {
console.log('Batch complete!');
for (const v of status.verifications || []) {
if (v.status === 'completed') {
console.log(` [${v.result.confidence}%] ${v.result.title}`);
} else {
console.log(` [FAILED] Paper ${v.batch_index + 1}: ${v.error}`);
}
}
break;
}
await new Promise(r => setTimeout(r, (status.poll_interval_seconds || 5) * 1000));
}
};
submitBatch();
Have questions or need assistance integrating the VerifyScience API? We're here to help!