The public API
Read-only, JSON, CORS-enabled, no key. Every endpoint is backed by the same functions the pages call — searchWorks() serves both /works and /api/v1/works — so the API cannot answer a different question from the page above it.
Base URL: https://metacan.xera.ac/api/v1
/api/v1/stats/summaryThe frame in one object: total works, the no-affiliation and no-abstract counts, the four route marginals, and the screen's consensus histogram.
curl -sS https://metacan.xera.ac/api/v1/stats/summary | jq/api/v1/worksBrowse and search the whole frame. Full-text over titles, every filter the browse page offers, paginated.
total_is_capped: true means “at least 10,000”, not “exactly 10,000”. Page through if you need more.| Parameter | Type | Meaning |
|---|---|---|
q | string | Full-text search over titles (Postgres tsvector; terms are ANDed). |
year_from, year_to | int | Inclusive publication-year bounds. |
lang | string | Language code, e.g. en, fr. |
type | string | Work type, e.g. article, preprint, dissertation. |
field | string | OpenAlex primary field, e.g. 'Medicine'. |
route | aff | fund | venue | about | no_aff | Route provenance: why the work is in the frame. no_aff returns the works with NO Canadian affiliation — the ones an affiliation-only frame never sees. |
retracted | 1 | Only works OpenAlex flags as retracted. |
no_abstract | 1 | Only works with no abstract. The screen finds half as much metaresearch here. |
n_in | 0..3 | Screening consensus: how many of the three models called it metaresearch. |
sort | cited | year_desc | year_asc | Default: cited. |
page, per_page | int | per_page max 100, default 25. |
# The works an affiliation-only frame would never have seen,
# most-cited first:
curl -sS "https://metacan.xera.ac/api/v1/works?route=no_aff&sort=cited&per_page=5" | jq '.results[] | {id, title, cited_by, routes}'
# French-language works with no abstract, published since 2015:
curl -sS "https://metacan.xera.ac/api/v1/works?lang=fr&no_abstract=1&year_from=2015&per_page=5" | jq
# Full-text search:
curl -sS "https://metacan.xera.ac/api/v1/works?q=reproducibility+crisis&per_page=3" | jq '.results[].title'/api/v1/works/{id}One work, every field, the routes that admitted it, its Retraction Watch state if any, and all three models' labels and reasons if it was screened.
| Parameter | Type | Meaning |
|---|---|---|
abstract | 1 | Fetch the abstract live from OpenAlex and de-invert it. Off by default: abstracts are not in this database, so asking for one costs an upstream round-trip. |
# A work, with its provenance:
curl -sS https://metacan.xera.ac/api/v1/works/W2342586781 | jq '{id, title, routes}'
# With the abstract fetched live from OpenAlex:
curl -sS "https://metacan.xera.ac/api/v1/works/W2342586781?abstract=1" | jq '.abstract'/api/v1/screenedThe 1,000 screened works with all three models' tiers, genres, confidences and reasons, plus the design weight.
weight (inverse selection probability). Any rate you compute from these rows without applying the weight is wrong.| Parameter | Type | Meaning |
|---|---|---|
contested_only | 1 | THE DISAGREEMENT DOSSIER: every work any model called metaresearch. This subset, not the base rate, is the project’s deliverable. |
n_in | 0..3 | Exact consensus count. |
stratum | string | e.g. aff_core, about_only, french, venue_new, fund_new. |
page, per_page | int | per_page max 100. |
# The disagreement dossier: the works that mark the field's boundary.
curl -sS "https://metacan.xera.ac/api/v1/screened?contested_only=1" | jq '.meta.summary'
# The works only ONE model called metaresearch:
curl -sS "https://metacan.xera.ac/api/v1/screened?n_in=1" \
| jq '.results[] | {title, opus: .opus.tier, gpt: .gpt.tier, grok: .grok.tier}'/api/v1/stats/by-routeThe four routes: marginals, and the exact route combinations.
marginals sums to more than the frame. combinations counts each work once and sums to the total.curl -sS https://metacan.xera.ac/api/v1/stats/by-route | jq '{no_aff: .meta.no_aff, marginals, combinations: .combinations[:5]}'/api/v1/stats/by-yearWorks per year, with the no-affiliation and no-abstract counts alongside, because both gaps move over time.
curl -sS https://metacan.xera.ac/api/v1/stats/by-year | jq '.results[-5:]'/api/v1/stats/by-fieldThe field breakdown, plus languages, the abstract gap by type, top venues, top funders, and the four-state retraction record — everything the analytics page draws, in one call.
curl -sS https://metacan.xera.ac/api/v1/stats/by-field | jq '.retraction_states'/api/v1/findingsAll 22 findings, served verbatim from the file the pilot scripts write. Every number quoted anywhere on this site comes from here.
curl -sS https://metacan.xera.ac/api/v1/findings | jq '.findings.three_model_screen.headline'Notes
- CORS is open (
*). This is a public CC-BY research dataset; the point of publishing it is that you can query it from your own page without proxying. - Caching. Responses carry
s-maxage=3600. The frame is a pinned snapshot: it does not change between deploys, so a stale aggregate is not a risk and re-scanning 4.3M rows per request would be. - No key, no rate limit — but it is one small server. Be reasonable, and if you need the whole frame, take the repository and rebuild it locally rather than paginating four million rows out of this box.
- Licence. Data CC-BY-4.0, code MIT. Cite OpenAlex and Retraction Watch as the upstream sources.