API reference
The Polygraph API.
JSON over HTTPS at https://beta.polygraphs.xyz/api/v1. Every response that fails is { error, code } with the status below.
Authentication, scopes and limits
Send a key (pg_…) as x-api-key: pg_… or Authorization: Bearer pg_…. A key acts for your account: what it can read is what you can read, and what it checks is charged to your wallet. A read key can get posts, claims and changes; a read + check key can also quote, check, re-verify and share. A key may carry a monthly credit cap; a charge that would pass it is refused with 402 key-cap-reached.
Per key: reads 60 a minute and 5,000 a UTC day; checks and re-verifies 10 a minute and 200 a day. Quote and share count as reads. Past a limit you get 429 rate-limited with a Retry-After header in seconds.
Connecting an AI assistant (OAuth) makes an ordinary pg_ key, named for the assistant: it is listed on your keys page, where you can cap or revoke it, and it works on this REST API like any other key.
Reads are free today. Coming: each key keeps 1,000 free reads a day, and a read past that costs 1 credit.
Cross-origin browser calls are allowed only from approved origins; call the API from your server and keep the key there.
Quote a check
POST/api/v1/quote
The credit price of a check (a post link or X post id) or a re-verify (a post id) before anything runs. Nothing is charged. Quoting a post we don't have yet fetches it, so the check that follows doesn't fetch it again.
Scope read + check · counts as a read · errors unauthorized, rate-limited, bad-input, unsupported-link, text-not-supported, fetch-failed, not-found, forbidden
curl -s -X POST https://beta.polygraphs.xyz/api/v1/quote \
-H "Authorization: Bearer $POLYGRAPH_KEY" \
-H "content-type: application/json" \
-d '{"kind":"check","input":"https://x.com/janedoe/status/1843123456789012345"}'{
"kind": "check",
"input": "https://x.com/janedoe/status/1843123456789012345"
}200 The price.
{
"contentId": "3f1c2a9e-7b4d-4e21-9c55-0d8e6a1b2c3d",
"slug": null,
"kind": "check",
"credits": 25,
"free": null,
"basis": {
"shape": "text",
"durationSec": null
},
"addOnPossible": false,
"reenrich": null,
"balance": 200,
"introEndsOn": "2027-01-01"
}Check a post
POST/api/v1/check
Check every claim in one post. A new check draws its price from your credits first (refunded if Polygraph decides the post makes no checkable claim). 202 while it runs; 200 when everything was already on file. Poll the post for the result.
Scope read + check · counts as a check · errors unauthorized, rate-limited, bad-input, unsupported-link, text-not-supported, fetch-failed, insufficient-credits, key-cap-reached, price-changed
curl -s -X POST https://beta.polygraphs.xyz/api/v1/check \
-H "Authorization: Bearer $POLYGRAPH_KEY" \
-H "content-type: application/json" \
-d '{"input":"https://x.com/janedoe/status/1843123456789012345"}'{
"input": "https://x.com/janedoe/status/1843123456789012345"
}202 A run started or is in flight.
{
"contentId": "3f1c2a9e-7b4d-4e21-9c55-0d8e6a1b2c3d",
"slug": "jane-doe-the-bill-cuts-3f1c2a",
"status": "pending",
"started": [
"claim-analysis",
"framing"
],
"runId": "run_cmg1x2y3z",
"credits": 25
}200 Everything was already on file; nothing ran or was charged.
{
"contentId": "3f1c2a9e-7b4d-4e21-9c55-0d8e6a1b2c3d",
"slug": "jane-doe-the-bill-cuts-3f1c2a",
"status": "complete",
"started": [],
"runId": null,
"credits": 0
}Get a post
GET/api/v1/posts/{contentId}
One post's results: per-stage status, the post, its claims with verdicts and sources, scores, the Polygraph score, the framing and rhetoric reads, related posts and reply drafts. Send the last ETag as If-None-Match and an unchanged post answers 304.
Scope read · counts as a read · errors unauthorized, rate-limited, bad-input, not-found, forbidden
| Parameter | In | Description |
|---|---|---|
contentId · required | path | The post's id (a UUID, from a check or a quote). |
If-None-Match | header | The ETag of your last read. |
curl -s https://beta.polygraphs.xyz/api/v1/posts/3f1c2a9e-7b4d-4e21-9c55-0d8e6a1b2c3d \
-H "Authorization: Bearer $POLYGRAPH_KEY"200 The post (abridged here).
{
"contentId": "3f1c2a9e-7b4d-4e21-9c55-0d8e6a1b2c3d",
"state": "done",
"stages": {
"route": {
"state": "done",
"send": true
},
"claims": {
"state": "done",
"count": 3
},
"framing": {
"state": "done"
},
"scores": {
"state": "done"
},
"replies": {
"state": "done"
}
},
"updatedAt": "2026-10-06T14:02:11.000Z",
"etag": "\"p-9c1e0a7f\""
}304 Unchanged since the ETag you sent.
Get a claim
GET/api/v1/claims/{claimId}
One claim with its latest verdict, confidence, summary, sources and verdict history, and the posts you can read that make it.
Scope read · counts as a read · errors unauthorized, rate-limited, bad-input, not-found, forbidden
| Parameter | In | Description |
|---|---|---|
claimId · required | path | The claim's numeric id (from a post's claims). |
curl -s https://beta.polygraphs.xyz/api/v1/claims/48213 \
-H "Authorization: Bearer $POLYGRAPH_KEY"200 The claim (abridged here).
{
"claimId": 48213,
"text": "The bill cuts school funding by 40 percent.",
"claimType": "statistic",
"temporalType": "current_state",
"verdict": "false",
"confidence": 0.91,
"summary": "The bill trims one grant program by 4 percent; total school funding rises. The 40 percent figure does not appear in the bill or its budget score.",
"verifiedAt": "2026-10-06T13:58:40.000Z",
"verifications": 1,
"appearancesTotal": 2
}Re-verify a post
POST/api/v1/posts/{contentId}/reverify
Check a checked post's claims again, against today's sources. Quote it first (kind reverify): it is charged like a check. 409 when nobody has checked the post yet.
Scope read + check · counts as a check · errors unauthorized, rate-limited, bad-input, not-found, forbidden, not-checked, insufficient-credits, key-cap-reached
| Parameter | In | Description |
|---|---|---|
contentId · required | path | The post's id (a UUID, from a check or a quote). |
curl -s -X POST https://beta.polygraphs.xyz/api/v1/posts/3f1c2a9e-7b4d-4e21-9c55-0d8e6a1b2c3d/reverify \
-H "Authorization: Bearer $POLYGRAPH_KEY"202 The re-verify started (or was already running).
{
"contentId": "3f1c2a9e-7b4d-4e21-9c55-0d8e6a1b2c3d",
"slug": "jane-doe-the-bill-cuts-3f1c2a",
"status": "pending",
"started": true,
"runId": "run_cmg4a5b6c",
"credits": 25
}List changes
GET/api/v1/changes
What changed in your checks (or one collection you can read), at least once, oldest first — a feed to poll instead of polling every post. Pass back `cursor` as `since`; `more: true` means poll again now.
Scope read · counts as a read · errors unauthorized, rate-limited, bad-input, not-found, forbidden
| Parameter | In | Description |
|---|---|---|
since | query | The cursor from your last call. Without it the feed starts seven days back. |
collectionId | query | A collection you own or lease. Default: your checks. |
limit | query | 1–500, default 100. |
curl -s https://beta.polygraphs.xyz/api/v1/changes \
-H "Authorization: Bearer $POLYGRAPH_KEY"200 A page of changes.
{
"changes": [
{
"contentId": "3f1c2a9e-7b4d-4e21-9c55-0d8e6a1b2c3d",
"state": "done",
"stages": {
"route": {
"state": "done",
"send": true
},
"claims": {
"state": "done",
"count": 3
},
"framing": {
"state": "done"
},
"scores": {
"state": "done"
},
"replies": {
"state": "done"
}
},
"updatedAt": "2026-10-06T14:02:11.000Z",
"etag": "\"p-9c1e0a7f\""
}
],
"cursor": "eyJ0IjoiMjAyNi0xMC0wNlQxNDowMjoxMS4wMDAifQ",
"more": false
}Errors
| Status | Code | When |
|---|---|---|
| 400 | bad-input | The request is malformed: a missing field, a bad id, a bad cursor. |
| 401 | unauthorized | No key, or the key is unknown, revoked or expired. |
| 402 | insufficient-credits | Your balance can't cover the price. The body carries `credits` and `balance`. |
| 402 | key-cap-reached | The charge would pass this key's monthly credit cap. The body carries `cap`, `spent`, `credits`. |
| 403 | forbidden | The key is not a Polygraph key, or the post or claim is not one you can read. |
| 403 | key-scope-required | A read key on quote, check, re-verify or share. The body carries `scope` and `required`. |
| 404 | not-found | No such post, claim or collection. |
| 409 | price-changed | The price rose above the quote you agreed to (`maxCredits`) before the check ran. Nothing was charged; quote again. |
| 409 | not-checked | Re-verify of a post nobody has checked yet. Check it instead. |
| 409 | not-ready | Share of a check that hasn't finished. |
| 409 | withdrawn | Share of a check Polygraph withdrew from the public site. |
| 422 | unsupported-link | A link, but not to a post on a platform Polygraph checks. |
| 422 | text-not-supported | Pasted words instead of a post link. |
| 429 | rate-limited | Past the key's per-minute or per-day limit. `Retry-After` says when to try again; the body carries `window`, `limit`, `callClass`. |
| 502 | fetch-failed | The platform did not return the post. Nothing was stored or charged. |