Books — entry 006 of 22

Crossref Metadata Search

Verified Jul 2026

Crossref's REST API is a keyless way to search and retrieve bibliographic metadata — titles, authors, funders, references, licensing — for the 160-million-plus scholarly works it has assigned DOIs to, spanning journal articles, books, chapters, and datasets. Anonymous requests land in a low-throughput default pool; adding a mailto contact parameter moves you into a faster, still-free "polite" pool, and a paid Metadata Plus subscription removes the concurrency cap for production integrations. It's run directly by the Crossref non-profit rather than a third party.

Crossref's REST API is a keyless way to search 160-million-plus scholarly works by bibliographic string and get back trimmed JSON metadata -- title, DOI, type -- via a `select=` parameter that shrinks the payload to only the fields asked for. A live GET this run against `/works?query.bibliographic=atlas+of+api&rows=1&select=title,DOI,type` returned a genuine 300 B single-result record; the byte-identical query with `select=` removed returned 1,915 B, so the trimming is real rather than cosmetic.

booksacademicdoimetadatacrossref
AuthenticationNone requiredCall it straight away — no key, no signup.
HTTPSSupportedTraffic is encrypted in transit.
CORSEnabledCallable directly from browser JavaScript.
PricingFreemiumA usable free tier exists, with paid plans for more volume.
FormatsJSONResponses can be requested as JSON.

GreatAPIs Score

Score71out of 100
Authentication25/25No authentication required
Pricing17/20Freemium tier available
Docs0/20No docs or spec available
Formats9/15Single response format
Freshness20/20Verified within 6 months

Embed this badge

Scored 71 on greatapis.com
<a href="https://greatapis.com/api/crossref-metadata-search/"><img src="https://greatapis.com/badge/crossref-metadata-search.svg" alt="Scored 71 on greatapis.com"></a>

Auth quickstart

  1. No API key, signup, or credit card required for the base search -- a live GET this run returned real results with zero credentials. Adding a `mailto=` query parameter (any real contact address) moves the same request into Crossref's faster "polite pool": a live GET this run with `mailto=test@example.com` on the identical query returned `x-rate-limit-limit: 3` / `x-api-pool: polite-array`, versus `x-rate-limit-limit: 1` / `x-api-pool: public-array` on the anonymous request moments before -- both still fully free, no key ever issued.
Stored keyNo key stored

Your key is stored only in this browser (localStorage) and sent directly to the API — never to greatapis.

Search scholarly works by bibliographic string, trimmed to three fields

GEThttps://api.crossref.org/works?query.bibliographic=atlas+of+api&rows=1&select=title,DOI,type

200 application/json

{"status":"ok","message-type":"work-list","message-version":"1.0.0","message":{"facets":{},"total-results":177895,"items":[{"title":["atlas: Stanford 'ATLAS' Search Engine API"],"DOI":"10.32614\/cran.package.atlas","type":"dataset"}],"items-per-page":1,"query":{"start-index":0,"search-terms":null}}}

`select=title,DOI,type` is what keeps this 300 B -- dropping it from this exact query, and changing nothing else, returned 1,915 B on a live GET this run. Separately, and not a `select=` comparison since that route rejects the parameter outright (see the gotchas): the single-work route returns each record in full by default -- abstract, references, funders, license terms -- which is why a live GET this run against `/works/10.1371/journal.pone.0000308` ran 11,960 B for that one work.

Try it

Developer reference

Base URLhttps://api.crossref.org
Rate limit

1 request/second anonymously (`x-rate-limit-limit: 1`, confirmed live this run); adding a `mailto=` contact parameter to enter the free "polite pool" raises this to 3 requests/second (`x-rate-limit-limit: 3`), confirmed live the same run. A separate `/works/{doi}` single-work lookup draws from its own pool at 5 requests/second.

Key endpoints
  • GET/works
  • GET/works/{doi}

Gotchas & limits

  • Anonymous and polite-pool requests carry genuinely **different rate limits in their own headers** -- confirmed live this run on the identical query: the anonymous call returned `x-rate-limit-limit: 1` / `x-rate-limit-interval: 1s` / `x-api-pool: public-array`, while adding `mailto=test@example.com` returned `x-rate-limit-limit: 3` / `x-api-pool: polite-array` -- three times the throughput for one added query parameter, no signup required.
  • A single-DOI lookup (`/works/{doi}`) draws from a **third, separate pool** -- a live GET this run against `/works/10.1371/journal.pone.0000308` returned `x-rate-limit-limit: 5` / `x-api-pool: public-single`, a different limit and pool name than either search variant above.
  • `select=` genuinely shrinks the payload, not just which keys are visible -- confirmed live this run with a like-for-like pair on one identical query: `/works?query.bibliographic=atlas+of+api&rows=1&select=title,DOI,type` returned **300 B**, while the same request with `select=` simply removed returned **1,915 B**. About a 6x saving on a single-result query, and it compounds as `rows=` grows.
  • `select=` is **rejected outright on the single-work route** -- a live GET this run against `/works/10.1371/journal.pone.0000308?select=title,DOI,type` returned a genuine **HTTP 400** with a 164-byte `{"status":"failed","message-type":"validation-failure","message":[{"type":"parameter-not-allowed","value":"select","message":"This route does not support select"}]}`. That same record fetched the only way that route allows -- no `select=` -- ran **11,960 B** live this run, so a per-DOI lookup is both fat and untrimmable. To get one work trimmed, query it through the search route instead: a live GET this run against `/works?filter=doi:10.1371/journal.pone.0000308&rows=1&select=title,DOI,type` returned the same work at **336 B**.
  • A DOI's own slash (e.g. `10.1371/journal.pone.0000308`) is passed as a **literal, unencoded path segment** after `/works/` -- confirmed live this run -- Crossref's own route parses everything after `/works/` as the DOI, so it doesn't need URL-encoding.