How to use it: 3 steps
- Make a free Apify accountApify is the cloud platform where our tools run. The free plan gives you $5 of usage every month, no credit card needed. You pay Apify, not us.
- Paste your input and press StartNo code needed: you fill in a form (the input). It already has a working example. For example:
10.1038/nature12373in DOIs. Each start is one run. - Download the resultsWhen the run ends, the results are in a table (the dataset). Open Output, press Export and pick Excel or CSV. JSON and the API are there too.
This tool is an Apify Actor: a small program that runs on Apify. You can also run it on a schedule, for example every Monday. New to it? Read how it works, step by step and the 8 words you need first.
What it does
Paste a list of DOIs and get one clean citation record per DOI. This bulk DOI metadata lookup checks each DOI at doi.org, reads the metadata from the official Crossref, DataCite or JaLC API, and returns title, authors, journal or book, exact dates, volume, pages, ISSN/ISBN, license and citation counts.
- Cleans your input. It accepts
10.…,doi:10.…,https://doi.org/10.…anddx.doi.orglinks, in any letter case. - Asks doi.org which agency registered each DOI.
- Reads the record from that agency: Crossref (most journals and books), DataCite (datasets, software, repositories) or JaLC (Japan).
- Writes one Dataset row for each input value, in your input order, with a clear
status.
Good for
- Turn a list of DOIs into a table of titles, authors, journals, years and pages.
- Check a reference list: find DOIs that are not registered or have a typo.
- Add ISSN, ISBN, publisher, license and citation counts to a list of papers.
- Mix journal articles and research datasets in one run (Crossref and DataCite).
Not a good fit
- No search by title or author. You need the DOI.
- No PDFs, full text or abstracts. The Actor reads only bibliographic metadata.
- No Open Access links (for example from Unpaywall).
- DOIs from other agencies (for example mEDRA) are only identified. The row has
status: unsupported_agencyand the agency name, but no title or authors. - No BibTeX or RIS file. You get structured fields that you can export as JSON, CSV or Excel.
- Up to 500 DOIs per run.
Who uses it
Researchers and students
Turn a DOI list into a reference table.
Librarians
Find typos and unregistered DOIs in a list.
Research offices
Add journals, ISSN and licenses to paper lists.
Price
Pay per event: you pay for each result the tool saves, plus a small start fee for each run. The price includes Apify platform costs. Apify bills you, not us. Prices below are for the Apify free plan.
| What you pay for | Price | When |
|---|---|---|
| Start fee | $0.001 | Charged once per run. This tool uses 1 GB of memory or less by default; more memory costs more. |
| DOI metadata record | $0.0018 (from $0.0015 on paid Apify plans) | One unique DOI with a metadata row saved to the Dataset: a complete row, or a partial row that has metadata but no title (NO_TITLE). Repeated DOIs are free. |
| DOI check without metadata | $0.0005 | One unique DOI checked without metadata: doi.org reports it as not registered, it belongs to an agency this Actor does not read, or the agency has no record yet (not_found, unsupported_agency or RECORD_NOT_INDEXED row). |
Examples
- One DOI record: about $0.0028.
- 1,000 DOI records: about $1.80.
- Every day 100 DOI records, for 30 days: about $5.43 a month.
Free to try: the Apify free plan gives you $5 of usage every month, no credit card needed. That is enough for about 2,700 DOI records.
Prices checked on 24 October 2026. See the current price on Apify.
Questions
Can I search by title?
No. You need the DOI.
Does it give abstracts or PDFs?
No. It gives bibliographic metadata only.
Why is a date only a year?
The tool keeps the source precision. It does not add a fake day.
Why is my citation count lower than Google Scholar?
It is the count from Crossref or DataCite. Other indexes count other sources.
Do I need an API key?
No. Crossref, DataCite, JaLC and doi.org are open. You only need your Apify account.
Why is citedByCount different from Google Scholar?
It is the count known to the registration agency (Crossref or DataCite). Other indexes count different sources.
How fresh is the data?
Each run reads the sources live. Each row has fetchedAt (our read time) and sourceUpdatedAt (when the agency last updated the record).
Details
Step by step on Apify
- Open the Input tab.
- Keep the three example DOIs in DOIs (
dois) or paste your own, one per line. - Leave Contact email empty, or add your own email for large runs.
- Click Start.
- Open the Output tab, view Citations. Check
statusandtitlein the first row.
Use cases
| Task | Input | You get | Key fields | Next step |
|---|---|---|---|---|
| Build a reference table | dois | One citation record per DOI | title, authors, containerTitle, issuedDate, pages | Export CSV/Excel, or read the Dataset by API |
| Clean a bibliography | dois | Status per DOI | status, statusReason, errorCode, doi | Filter rows where status is not complete |
| Enrich a paper list | dois | Identifiers and counts | issn, isbn, publisher, licenses, citedByCount | Join on doi (always lower case) |
| Catalogue research data | dois | DataCite dataset record | resourceTypeGeneral, publisher, publicationYear, language | Export or API |
Tips for better results
- Paste DOIs in any form, like doi: or a doi.org link.
- Filter rows that are not complete to clean your list.
- Join on the DOI column. It is always lower case.
Limits and good to know
- Up to 500 DOIs per run. In tests, 500 DOIs finished in about 24 seconds and 100 DOIs in about 6 seconds
- The Actor reads sources for up to 240 seconds. DOIs not processed in time get a free
errorrow withDEADLINE_REACHED - It sends batch requests (up to 50 DOIs each) and retries temporary failures. It respects source rate limits
Troubleshooting
| Symptom | Check | Action |
|---|---|---|
not_found for a DOI you trust | input, doi | Look for a typo or extra characters. doi.org itself says the DOI is not registered. |
invalid row | input, statusReason | After cleaning, the value is not in the form 10.<prefix>/<suffix>. Fix the value. |
partial with RECORD_NOT_INDEXED | statusReason | The DOI is registered, but the agency API does not have the record yet. Try again later. |
partial with NO_TITLE | type | Records such as journal issues or figures often have no title. This is source data. |
unsupported_agency | agency | The DOI belongs to another agency. This Actor does not read its metadata. |
error rows | errorCode, RUN_SUMMARY.warnings | A source was down or slow. Run only these DOIs again later; they were not charged. |
| Run rejected at start | Input | dois is empty or has more than 500 values. |
For developers
Sample result
One result from a real test run, in JSON. Real values change from run to run. In Excel or CSV each field is a column.
{
"input": "10.1038/nature12373",
"doi": "10.1038/nature12373",
"doiUrl": "https://doi.org/10.1038/nature12373",
"status": "complete",
"statusReason": null,
"agency": "Crossref",
"type": "journal-article",
"title": "Nanometre-scale thermometry in a living cell",
"authors": [
{ "given": "G.", "family": "Kucsko", "name": "G. Kucsko", "orcid": null, "affiliations": [] }
],
"containerTitle": "Nature",
"publisher": "Springer Science and Business Media LLC",
"issuedDate": "2013-07-31",
"publishedPrintDate": "2013-08",
"publicationYear": 2013,
"volume": "500",
"issue": "7460",
"pages": "54-58",
"issn": [
{ "value": "0028-0836", "type": "print" },
{ "value": "1476-4687", "type": "electronic" }
],
"authorsCount": 8,
"provider": "Crossref REST API",
"errorCode": null
}Use it from code
Actor ID: cliqtomedia/doi-bibliography-enrichment. You need your Apify API token from Apify Console → Settings → API & Integrations.
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("cliqtomedia/doi-bibliography-enrichment").call(run_input={
"dois": [
"10.1038/nature12373",
"10.5061/dryad.8515",
"10.1007/978-3-319-24277-4_9"
]
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item)curl -X POST "https://api.apify.com/v2/acts/cliqtomedia~doi-bibliography-enrichment/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"dois": ["10.1038/nature12373", "10.5061/dryad.8515", "10.1007/978-3-319-24277-4_9"]}'# pip install langchain-apify
# set APIFY_TOKEN in your environment
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("cliqtomedia/doi-bibliography-enrichment")
result = tool.invoke({"run_input": {"dois": ["10.1038/nature12373", "10.5061/dryad.8515", "10.1007/978-3-319-24277-4_9"]}})
print(result)The cURL call waits for the results, up to 300 seconds. For longer runs, start a run (/runs) and get the results with a webhook or by checking the run. Apify API docs. LlamaIndex, MCP for AI agents and the JavaScript client: AI agents & MCP.
Data fields (37)
| Field | Type | Meaning |
|---|---|---|
input | string | Your input value exactly as given. |
doi | string | Canonical DOI in lower case (DOIs are case-insensitive); null for invalid input. |
doiUrl | string | Resolvable https://doi.org/ link. |
status | string | complete: metadata found. partial: DOI exists but the record is missing or has no title. not_found: doi.org says the DOI is not registered. invalid: not a DOI. unsupported_agency: registered with another agency (agency is given). error: a source failed; existence unknown. |
statusReason | string | Plain-English explanation for any status other than complete. |
agency | string | Registration agency from doi.org: Crossref, DataCite, JaLC, mEDRA, … |
type | string | Work type as the agency names it, e.g. journal-article, book-chapter, proceedings-article, dataset. |
resourceTypeGeneral | string | DataCite general resource type, e.g. Dataset, Software, Text. |
title | string | Title with markup removed and HTML entities decoded. |
subtitle | string | Subtitle, if the record has one. |
authors | array | Authors in record order: given, family, full name, ORCID URL (only when the source lists it), affiliations. |
editors | array | Editors, same shape as authors. |
containerTitle | string | Journal, book or proceedings that contains the work (for a book chapter, the book). |
containerTitles | array | All container titles exactly as the agency lists them. |
seriesTitle | string | Book series, e.g. Use R!, when the record lists one. |
publisher | string | Publisher. |
issuedDate | string | Publication date at the precision the source gives: 2016, 2013-08 or 2013-07-31. Never padded. |
publishedOnlineDate | string | Online publication date (Crossref), same precision rule. |
publishedPrintDate | string | Print publication date (Crossref), same precision rule. |
publicationYear | integer | Publication year. |
volume | string | Volume. |
issue | string | Issue. |
pages | string | Page range, e.g. 54-58. |
articleNumber | string | Article number for journals without page numbers. |
issn | array | ISSNs with type print/electronic when known. |
isbn | array | ISBNs with type print/electronic when known. |
licenses | array | License statements from the record (Crossref license / DataCite rightsList). |
referencesCount | integer | Number of references deposited with the record. |
citedByCount | integer | Citations known to the agency (Crossref is-referenced-by-count / DataCite citationCount). |
language | string | Language code when the record states it (DataCite, JaLC; not returned by Crossref batch queries). |
landingUrl | string | Publisher landing page registered for the DOI. |
sourceUpdatedAt | string | When the agency last indexed or updated the record. |
authorsCount | integer | Number of authors. |
provider | string | API the row came from. |
sourceUrl | string | API request the metadata was read from. |
fetchedAt | string | When this row was produced (ISO 8601). |
errorCode | string | Machine code: INVALID_DOI, DOI_NOT_REGISTERED, UNSUPPORTED_AGENCY, RECORD_NOT_INDEXED, NO_TITLE, SOURCE_UNAVAILABLE, RATE_LIMITED, DEADLINE_REACHED, … |
Input settings (2)
The main fields of the input form. With the API you send them as JSON.
| Setting | Name in the API | Type | Example value |
|---|---|---|---|
| DOIs required | dois | array | ["10.1038/nature12373", "10.5061/dryad.8515", "10.1007/978-3-319-24277-4_9"] |
| Contact email for Crossref and DataCite (optional) | contactEmail | string |
