Reference List Verifier: Crossref DOI Checker
Pricing
$1.00 / 1,000 reference checkeds
Reference List Verifier: Crossref DOI Checker
Paste a bibliography and find the closest Crossref or OpenAlex record for each reference: DOI, match status and score, retraction or correction flag from Crossref. Pay per reference.
Pricing
$1.00 / 1,000 reference checkeds
Rating
0.0
(0)
Developer
Mohammed Samaan
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
Paste a bibliography (or a list of references) and get one row per reference: the closest record found in Crossref or OpenAlex, its DOI, a match status, a score, and a retraction or correction flag when Crossref itself records one.
It reads references the way they are written in papers and theses, in any citation style, with or without numbering. It does not need DOIs up front. If a reference already contains a DOI, the DOI is checked directly.
What it tells you, and what it does not. A row says that a close record was or was not found. It never says a reference is correct. A not_found reference may still be real (a book, a thesis, a web page, a very new paper); a matched reference should still be read by a human before you rely on it.
Who uses it
- Students and supervisors checking a thesis bibliography for typos, wrong years and invented citations.
- Editors and reviewers screening a manuscript's reference list.
- Teams that use an AI writer and want to catch references the model made up.
- Anyone who needs the DOI for a pile of plain-text references.
Quick start
- Paste references into References, one per line, or paste the whole list into Or paste a whole bibliography.
- Add your email in Your contact email (recommended, see "Speed and politeness").
- Run. Results appear in the dataset, one row per reference, in input order.
Sample input
{"references": ["LeCun, Y., Bengio, Y., & Hinton, G. (2015). Deep learning. Nature, 521(7553), 436-444.","Hirsch JE (2005) An index to quantify an individual's scientific research output. Proc Natl Acad Sci USA 102:16569-16572 doi:10.1073/pnas.0507655102"],"minScore": 0.6}
Or a pasted list:
{ "text": "[1] LeCun Y, Bengio Y, Hinton G. Deep learning. Nature 2015;521:436-44.\n[2] Wakefield AJ, et al. Ileal-lymphoid-nodular hyperplasia, non-specific colitis, and pervasive developmental disorder in children. Lancet 1998;351:637-41." }
Sample output (real run, 21 Oct 2026, shortened)
{ "index": 1, "status": "matched", "doi": "10.1038/nature14539", "title": "Deep learning", "year": 2015,"score": 1, "titleSimilarity": 1, "authorMatch": true, "yearMatch": true,"isRetractedOrCorrected": null, "source": "crossref", "matchedBy": "text","inputDoi": "10.1038/nature1453999", "warnings": ["input_doi_not_found"] }{ "index": 2, "status": "matched", "doi": "10.1016/s0140-6736(97)11096-0","title": "RETRACTED: Ileal-lymphoid-nodular hyperplasia, non-specific colitis, and pervasive developmental disorder in children","score": 1, "isRetractedOrCorrected": true, "source": "crossref", "matchedBy": "text" }{ "index": 3, "status": "not_found", "doi": null, "score": 0.395,"closest": { "doi": "...", "title": "Border makers: clandestine migration from Morocco", "score": 0.395 } }{ "index": 4, "status": "error", "error": "Not a recognisable reference: it needs at least 4 words (author, title, year, source) or a DOI." }
In row 1 the DOI typed in the reference had a typo; the actor reports that, then finds the right paper from the text.
Input
| Field | Meaning |
|---|---|
references | List of reference strings. Up to 500 per run, together with text. |
text | A pasted bibliography. Split on blank lines, on [1], (1), 1., 1) numbering, or one reference per line. Indented or lower-case-start lines are joined to the previous reference. |
mailto | Optional contact email for Crossref and OpenAlex (their "polite pool"). Faster, and recommended. Used only for those requests. |
minScore | Lowest score reported as probable (default 0.6, range 0.3 to 0.95). |
useOpenAlex | Ask OpenAlex when Crossref finds nothing close (default on). |
openalexApiKey | Optional free OpenAlex key (secret). |
Duplicate references (same text ignoring case and spacing) are looked up once and charged once; every input row still gets its answer, and repeats carry a duplicate_of_row_N warning. More than 500 references, a non-text entry, an invalid email or an out-of-range minScore stops the run with a message naming the field.
Output fields
| Field | Meaning |
|---|---|
input | The reference as checked (squashed to one line, cut at 1000 characters with a warning). |
status | matched, probable, not_found or error. |
doi, url, title, authors, year, container, type | The record found (null or empty for not_found and error). Abstracts are never returned. |
score | 0 to 1, our own score of how well the record fits your reference. For not_found it is the score of the closest record. |
titleSimilarity | Share of the record's title words that appear in your reference. |
authorMatch, yearMatch | true/false, or null when there was nothing to compare (no authors on the record, no year in your reference). |
isRetractedOrCorrected | true only when Crossref's own updated-by or update-to fields (or a retraction/correction relation) carry a notice of type retraction, partial retraction, withdrawal, removal, correction, corrigendum, erratum or expression of concern. If you matched the notice itself, the flag is true and recordType is notice. Otherwise null: "no notice found", never "confirmed clean". Always null for OpenAlex rows. |
updateNotices | The Crossref notices behind the flag: direction, type, doi. |
source | crossref or openalex. |
recordType | article, or notice when the matched record is itself a correction, erratum or retraction notice (it has an update-to field). |
matchedBy | doi (DOI in your text resolved to a fitting record) or text (search). |
inputDoi | The DOI found in your text, normalised (doi.org URLs, doi: prefixes and trailing punctuation are handled). |
closest | On not_found: the nearest record seen. Use it to see why nothing was accepted. |
warnings | See below. error holds the message when status is error, and errorKind says why: unreadable (not a usable reference), upstream (Crossref unreachable), charge, unexpected. |
Warnings: input_doi_not_found (the DOI in your text does not exist in Crossref or OpenAlex), input_doi_resolves_to_different_work (it exists but belongs to another paper), crossref_unavailable, openalex_unavailable, input_truncated.
How matching works
For each reference the actor looks up candidates (DOI lookup, else Crossref bibliographic search, else OpenAlex search) and scores each one itself:
- Title similarity (weight 0.60): the share of the record's distinctive title words found in your reference. Common words (the, of, and...) are ignored.
- Author agreement (0.25): the record's first author's family name appears in your reference (1.0), another of its first six authors does (0.6), none does (0). A record with no author list counts as neutral (0.5) and can never be
matched. - Year agreement (0.15): a four-digit year in your reference is within one year of the record's issued, print or online year. Left out of the score when your reference has no year.
| Status | Rule |
|---|---|
matched | Title similarity at least 0.90, at least one author agrees, year does not disagree, score at least 0.85. Titles with fewer than 3 distinctive words also need the first author to agree. |
probable | Title similarity at least 0.60, score at least minScore, the year does not disagree by more than one year, and corroboration: an author agrees, or the year agrees on a title of 4 or more distinctive words. |
not_found | Anything else, including a year that disagrees by more than one year. |
error | The reference could not be checked: unreadable input, or Crossref could not be reached after retries. Not charged. |
A reference that contains a DOI is first checked against that DOI's record with the same rules. If the DOI does not exist or belongs to a different paper, the actor says so in warnings and falls back to searching by the text without the DOI.
Accuracy, measured
Live probe on 21 Oct 2026 (Crossref public pool): 20 real references in APA, Vancouver, IEEE, Springer and journal-specific styles; 5 invented references; 3 variants of real references with the year changed by 6, 10 and 10 years.
| Test | Result |
|---|---|
| 20 real references | 19 matched with the correct DOI. 1 returned the wrong DOI as probable (the FAIR Guiding Principles paper, cited with an article number: Crossref did not return the right record in its top 10 and we accepted a related Nature Genetics item, 10.1038/ng.3544). 0 not_found. |
| 5 invented references | 0 matched, 0 probable, 5 not_found. |
| 3 real references with the year wrong by more than one year | 0 accepted; all 3 not_found (the record is shown in closest). |
In this sample there were 0 false matched and 1 wrong-DOI probable. Twenty-eight references is a small sample, not a guarantee. An earlier build accepted a real title and authors with a wrong year as probable; a year that disagrees by more than one year now always gives not_found, so a genuine reference with a mistyped year also lands there, with the real record in closest. Expect misses on books, theses, conference papers without DOIs and references with missing or abbreviated titles.
Speed and politeness
Without a contact email the actor sends no contact in its User-Agent, and the public pool is shared with everyone else using Crossref without one, so a contact email is recommended. Crossref has two access levels. With mailto, the actor sends your address in the User-Agent and request (Crossref's polite pool) and checks 3 references at a time. Without it, the actor uses the public pool: one request at a time, spaced out, so 500 references can take several minutes. Rate-limit and server errors (HTTP 429, 5xx) are retried with back-off up to 4 times, then reported as error (not charged). OpenAlex has no retraction data and, without an API key, shares a small daily budget per network address; if it is out of budget, the Crossref answer stands and openalex_unavailable is added to warnings.
Pricing
Pay per event: $0.001 per reference that got a definite answer (matched, probable or not_found) - event name reference-checked. Rows with status: "error" are not charged, and duplicates of a reference in the same run are charged once. Example: 200 references with 3 errors costs 197 x $0.001 = $0.197 plus Apify platform usage, which for this actor is small (plain HTTP, 256 MB). If you set a maximum charge for the run and it is reached, the run stops, says so in its status message and in the OUTPUT summary (stoppedBecause: charge_limit_reached), and the remaining references are left unchecked. If a charge itself fails, that row becomes an error row (errorKind: charge, result withheld), the run stops and fails with the message; this is reported separately from the charge limit.
Run safeguards. The run stops and fails after 8 consecutive Crossref failures (stoppedBecause: upstream_outage) or after 50 minutes (time_budget), and says how many references were not checked. Unreadable lines do not count towards the outage limit.
Use with AI agents and MCP
Send { "references": ["<one reference string>", "..."], "mailto": "<contact email>" }. Read status first, then doi and isRetractedOrCorrected. Treat matched as "a close record exists", treat probable as "needs a human look", and never present not_found as "fake": say "no close record found in Crossref or OpenAlex". The run summary (counts per status) is in the key-value store record OUTPUT.
FAQ
Does matched mean my reference is right? No. It means a record with nearly the same title, a matching author and a compatible year exists. Page numbers, volume and journal are not compared.
Why is isRetractedOrCorrected null and not false? Crossref's notices are incomplete, and OpenAlex does not provide them. null means "no notice found", not "verified clean".
Can I check a reference without authors or year? Yes, but the evidence is thinner, so it is more likely to be probable or not_found.
Is my list stored? The actor keeps nothing beyond the run's input, dataset and summary. References and the optional email go only to Crossref and OpenAlex.
Limits (honest)
- Coverage is whatever Crossref and OpenAlex hold. Books, theses, reports, preprints and web pages are often missing or have no usable title.
- Matching uses title words, first-author family name and year. It does not read journal, volume or pages, so two papers with near-identical titles by the same authors in the same year can be confused.
- Very short titles (fewer than 3 distinctive words) need an author match to count.
- Up to 500 references per run; each is cut at 1000 characters.
- Chinese, Japanese and Korean references are supported: unspaced text is compared as character pairs. Coverage and ranking for those languages were tested only on a recorded example, not on a live sample.
- The retraction flag depends on Crossref's own update records (which include the Retraction Watch data Crossref publishes) and may lag behind a journal's website.
- No abstracts, no full texts, no citation graph.
Open-source credits
| Component | Licence | Checked in |
|---|---|---|
apify 3.7.2 (Apify SDK) | Apache-2.0 | node_modules/apify/LICENSE.md |
| Transitive dependencies (187 entries in the lockfile; 169 installed without optional packages and all read; 167 of them carry a plain licence field) | MIT, ISC, BSD-2/3-Clause, Apache-2.0 | each package's package.json |
pause-stream | MIT and Apache-2.0 dual ("Dual Licensed MIT and Apache 2") | its LICENSE |
sax | BlueOak-1.0.0 | its package.json |
tslib | 0BSD | its package.json |
type-fest | MIT or CC0-1.0 | license-mit, license-cc0 |
caniuse-lite | CC-BY-4.0 (browser-support data, attribution: Alexis Deveria; pulled in by the SDK's crawling library, not used by this actor) | its package.json |
map-stream | no licence field; MIT text in its LICENCE file | its LICENCE |
Data sources: Crossref REST API (metadata of scholarly works, freely reusable facts) and OpenAlex (CC0). Matching code is original: token overlap, no fuzzy-matching library.