Skip to main content
v2026.11,610 entries · CC-BY 4.0

Using the PubMed E-utilities API: ESearch, EFetch, ELink and the 9,999-Record Ceiling

A measured, end-to-end walkthrough of pulling a department or author publication set from PubMed over HTTP: ESearch, EFetch, ELink, EPost and the History server, the real retrieval ceiling and batch caps, the observed rate limits, and the field tags E-utilities silently discards.

Ask about Using the PubMed E-utilities API: ESearch, EFetch, ELink and the 9,999-Record Ceiling

Answers are drawn from this guide and the rest of the CASRAI corpus, with a link to every source.

Answers are AI-generated from CASRAI’s own published pages and can be wrong, so check the linked sources before relying on one; your question is logged without personal data — never sold, never used to train a third-party model — to show us what CASRAI is missing, so please do not type personal or confidential details. How we use this

Written and maintained by CASRAI Editorial Board

Last updated

The research-office version of this task is always the same: someone needs the publication set — everything a department, a centre or a named investigator published in a window — and they need it as rows, not as a browser tab. E-utilities is the National Library of Medicine’s answer to that, and it is free, unauthenticated by default, and stable enough that scripts written a decade ago still run.

It is also almost completely silent when you get it wrong. That is the single most important thing to know before you point a script at it, so it is the first section on this page rather than a footnote near the bottom. A PubMed query that is malformed does not usually fail. It returns a number, and the number looks fine.

How to read this page

Three tiers, kept separate throughout, the same way as CASRAI’s companion pages on the Advanced Search Builder and Search Details and on how MeSH explosion and subheadings behave:

  1. Documented — stated by NCBI in Entrez Programming Utilities Help (the E-utilities book on NCBI Bookshelf), which is the authoritative specification.
  2. Measured — obtained by running the request against the live E-utilities service on 26 August 2026. Just over seventy live requests underpin this page, paced under the documented rate limit. PubMed grows daily, so every absolute count is a snapshot; the ratios and the behaviours are the durable findings.
  3. Observed only — reproducible behaviour NCBI has not specified in writing, or that contradicts what the documentation says. Flagged as such rather than presented as a rule.

For reference, the index we were querying: EInfo reported db=pubmed at 41,060,034 records, build Build-2026.08.26.12.02, with 48 indexed fields and 47 link names.

The hazard: your script can be searching something other than what you wrote

PubMed’s web interface warns you about a bad field tag. It puts the offending text in bold red type in the Search Details panel. E-utilities does not do this, and the difference matters more in a script than it does at a keyboard, because in a script nobody is looking.

An invalid field tag is discarded, not rejected

Measured, 26 August 2026. Four ESearch calls against db=pubmed:

term= Records querytranslation begins
crispr[tiab] 64,360 "crispr"[Title/Abstract]
crispr[abstract] 70,286 "clustered regularly interspaced short palindromic repeats"[MeSH Terms] OR …
crispr[zzz] 70,286 identical to the above
crispr (untagged) 70,286 identical to the above

There is no [abstract] tag in PubMed — the Title/Abstract field is [tiab]. But crispr[abstract] did not return zero, and it did not return an error. It returned the same 70,286 records as an untagged search, because the tag was thrown away and the bare word went through Automatic Term Mapping across all fields. A nonsense tag, [zzz], behaved identically. The invalid tag widened the result by 5,926 records while leaving a line in the code that reads as though it were field-limited.

This is the same behaviour the Advanced Search Builder page measured on sepsis in the same week, reproduced here on a different term, and it is worth stating the shape of the failure plainly: the direction of the error is towards more results, not fewer. Zero results get investigated. A larger-than-expected number gets shipped.

The response has a field for exactly this error, and it never fires

Observed only. ESearch’s JSON response carries esearchresult.errorlist.fieldsnotfound. On every invalid-tag request above it came back as an empty array. It is not that the error is reported somewhere inconspicuous; it is not reported at all.

The neighbouring error channels do work, which is what makes the gap easy to miss:

  • zzzqqxx[tiab] — a valid tag with a term that does not exist — returned 0 with errorlist.phrasesnotfound: ["zzzqqxx"].
  • "quantum sepsis pancake"[tiab] returned 0 with warninglist.quotedphrasesnotfound populated.
  • crispr[zzz] returned 70,286 with both lists empty.

So a script that checks errorlist before trusting a count will catch a typo in your search term and will not catch a typo in your search field.

The same silence applies to parameters, not just tags

Measured. ESearch has a field parameter, an alternative to bracket tags that applies a field to the whole query. It fails the same way, and a misspelled parameter name fails worse:

Request Result
term=crispr&field=tiab 64,360 — correct.
term=crispr&field=abstract 70,286 — parameter ignored.
term=crispr&field=zzz 70,286 — parameter ignored.
term=crispr[ti]&retmaxx=0 HTTP 200; retmax came back as 20 and the response carried 20 UIDs out of 23,130. The typo’d parameter was dropped and the default silently applied.

That last row is the one that costs people a day. A paging loop written against a mistyped parameter name does not crash and does not warn — it quietly collects the first page over and over, or stops after 20 records and reports success.

Errors arrive with HTTP 200

Observed only, and confirmed three separate ways. E-utilities returns application-level errors inside a 200 response body rather than as HTTP status codes:

  • db=pubmeddHTTP 200, body {"esearchresult":{"ERROR":"Invalid db name specified: pubmedd"}} — no count key at all.
  • An oversized ESummary request → HTTP 200, body {"error":"Too many UIDs in request. Maximum number of UIDs is 500 for JSON format output."}.
  • retstart past the ceiling → HTTP 200 carrying a Search Backend failed exception string.

Rate limiting is the exception: that one does return a real 429 (see below). Everything else means response.raise_for_status() is not a sufficient check. Parse the body and look for ERROR and error keys before you touch count.

The defence: read back querytranslation

There is a reliable programmatic check, and it costs nothing because the value is already in the response you just received. Every ESearch response carries querytranslation, the query PubMed actually executed. If your field tag survived, the translation contains the expanded field name in PubMed’s own vocabulary — "crispr"[Title/Abstract], "Oxford"[Affiliation], "0000-0002-2561-2113"[Author - Identifier]. If it was discarded, you get an Automatic Term Mapping expansion ending in [All Fields] instead.

So the rule for any scripted PubMed query is: assert on the translation, not on the status code. Fail the run if querytranslation does not contain the field name you intended. EInfo (einfo.fcgi?db=pubmed&retmode=json) will give you the 48 field records with their fullname values — AUID is "Author - Identifier", TIAB is Title/Abstract — which is what those translations are printed in. Observed caveat: EInfo’s 48 short names are the internal index names and are not a one-for-one list of PubMed’s bracket tags, so treat EInfo as a source for the expanded names to assert against, not as a tag whitelist.

The eight utilities, and the three you will actually use

Documented. All of them live under the same base URL, https://eutils.ncbi.nlm.nih.gov/entrez/eutils/, and take db, retmode and their own parameters as an ordinary query string.

Utility What it does Where it fits in a publication pull
esearch.fcgi Runs a query, returns a count and a list of UIDs (PMIDs). Step 1, always.
efetch.fcgi Returns full records for a UID set. Step 2, when you need abstracts, MeSH, author affiliations, grant numbers.
esummary.fcgi Returns a short document summary per UID. Step 2 alternative — far lighter, and JSON.
elink.fcgi Returns UIDs in one database linked to UIDs in another. PubMed → PMC full text, PubMed → related articles.
epost.fcgi Uploads a UID list to the History server. When your starting point is a list of PMIDs from a CV or a CRIS export, not a query.
einfo.fcgi Lists databases, their indexed fields and their link names. Validation and discovery.
espell.fcgi Spelling suggestions. Rarely.
ecitmatch.fcgi Resolves citation strings to PMIDs. Matching a reference list against PubMed.

The research-office task, end to end

The worked example below is a real one, run live: every publication carrying a University of Oxford affiliation string with a 2025–2026 publication date. Substitute your own institution and it is the same four calls.

Step 1 — ESearch with usehistory

Documented: "When usehistory is set to ‘y’, ESearch will post the UIDs resulting from the search operation onto the History server so that they can be used directly in a subsequent E-utility call." What comes back is a webenv string and an integer querykey, which together name the result set on NCBI’s side so you never have to send the UID list back.

GET esearch.fcgi?db=pubmed&retmode=json&retmax=0&usehistory=y
    &term=%22University+of+Oxford%22[ad]+AND+2025:2026[dp]
    &tool=your-app&[email protected]

Measured response, trimmed:

{"esearchresult":{
  "count":"16896",
  "retmax":"0","retstart":"0","idlist":[],
  "querykey":"1",
  "webenv":"MCID_6a8f3df8f082124ba30d6364",
  "querytranslation": <see below>
}}

The querytranslation value, with its JSON string-escaping unwound for readability, was:

"University of Oxford"[Affiliation] AND 2025/01/01:2026/12/31[Date - Publication]

Three things to note in that response before moving on. retmax=0 is deliberate — you want the count and the history handle, not a first page you are going to discard. The translation confirms the affiliation tag bound and shows how 2025:2026[dp] was expanded into explicit dates. And 16,896 is already a problem, for reasons the next section covers.

Step 2 — EFetch from the history handle, in batches

With WebEnv and query_key in hand, every subsequent call pages the stored set with retstart and retmax instead of shipping thousands of PMIDs in a URL:

GET efetch.fcgi?db=pubmed&query_key=1&WebEnv=MCID_6a8f3df8f082124ba30d6364
    &retstart=0&retmax=100&retmode=xml
    &tool=your-app&[email protected]

Measured: that single call returned 2,057,551 bytes of PubMed XML containing 100 <PubmedArticle> records and 1,198 <Author> elements. Roughly 20 KB per record is a useful planning figure: a 10,000-record pull is about 200 MB of XML.

Observed: EFetch for db=pubmed has no JSON output, and it does not say so. efetch.fcgi?db=pubmed&id=42644503&retmode=json returned HTTP 200 with the body 42644503 — a bare UID list, silently substituted. If you want JSON, use ESummary. If you want the full record, you are parsing XML, or asking for rettype=medline&retmode=text and parsing MEDLINE tag format.

Step 3 — ESummary when you only need the citation

For a publication list — author, title, journal, year, DOI, PMCID — ESummary is the right call, and it speaks JSON:

{"result":{"22745249":{
  "title":"A programmable dual-RNA-guided DNA endonuclease in adaptive bacterial immunity.",
  "source":"Science",
  "pubdate":"2012 Aug 17",
  "elocationid":"doi: 10.1126/science.1225829",
  "articleids":[{"idtype":"pmc","value":"PMC6286148"}]
}}}

The articleids array is where the crosswalk lives: PMID, DOI, PMCID and publisher IDs in one place. If you are reconciling a departmental list against a bibliographic API such as OpenAlex or against your CRIS, that array is the join key. CASRAI’s PMID vs PMCID comparison covers why those two are not interchangeable and why only one of them implies free full text.

Step 4 — EPost when you start from a list, not a query

A CV, a CRIS export or a grant progress report gives you PMIDs, not a search. EPost pushes them onto the same History server and returns the same two handles, after which every other utility works identically:

POST epost.fcgi
db=pubmed&id=22745249,31978945,32015507&tool=your-app&[email protected]

<ePostResult><QueryKey>1</QueryKey><WebEnv>MCID_6a8f3eb7d195a6e7e30b92c1</WebEnv></ePostResult>

The 9,999-record ceiling — and why WebEnv does not lift it

This is the most consequential correction on this page, because the usual advice — "use the History server for sets too large to page" — is only true up to a point, and that point is lower than the documentation says.

Documented: NCBI’s parameter reference states that ESearch returns UIDs "up to a maximum of 10,000 records," that EFetch retrieves "up to a maximum of 10,000," and, for PubMed specifically, that "ESearch can only retrieve the first 10,000 records matching the query."

Measured, and it is 9,999, not 10,000:

Request What came back
ESearch retmax=100000 HTTP 200. Response reported retmax: "9999" and carried exactly 9,999 UIDs. No error, no warning — the request was silently clamped.
ESearch retstart=9999 Search Backend failed: Exception: 'retstart' cannot be larger than 9998. For PubMed, ESearch can only retrieve the first 9,999 records matching the query.
EFetch from a WebEnv handle, retstart=10000 Same error. Search backend cannot retrieve history data. Reason: Exception: 'retstart' cannot be larger than 9998.
EFetch from history, retstart=9990&retmax=10 9 UIDs, not 10 — records 9,991 through 9,999. The set stops there.

So the History server saves you from putting 9,999 PMIDs in a URL. It does not give you access to the 10,000th. And the ceiling is one record below the documented figure, which is exactly the kind of off-by-one that turns into a silent truncation in a paging loop written to the documentation.

Note also that NCBI’s own error text names the workaround: "consider using EDirect, that contains additional logic to batch PubMed search results automatically." EDirect is NCBI’s command-line client for the same service; it slices the query for you.

The workaround inside the API: slice by date

If you are staying in HTTP, the only reliable way past the ceiling is to partition the query into slices that each return fewer than 9,999, and the natural partition for a publication pull is the publication date. Measured on the same affiliation query:

Query Records Retrievable?
"University of Oxford"[ad] AND 2025:2026[dp] 16,896 No — 6,897 records unreachable.
"University of Oxford"[ad] AND 2025[dp] 10,279 Still no — 280 records unreachable.
"University of Oxford"[ad] AND 2025/01:2025/03[dp] 3,553 Yes.

The middle row is the trap. A single year of a large research university’s output already exceeds the ceiling, and it exceeds it by only 280 records — a shortfall small enough that nobody notices the list is short. Do not choose your slice width by guessing. Run ESearch with retmax=0 first, read count, and subdivide until every slice is under 9,999. That check costs one cheap request per slice and is the difference between a complete publication set and a plausible one.

For genuinely institution-scale work — a full historical output, a bibliometrics baseline — the API is the wrong tool regardless of slicing, and NLM’s annual PubMed baseline files or a bulk source such as OpenAlex are the right ones.

Batch sizes: three separate limits, only one of them documented

Documented: for EPost, ESummary and EFetch, "if more than about 200 UIDs are to be provided, the request should be made using the HTTP POST method."

Measured, sending explicit UID lists to ESummary:

UIDs Method Format Result
300 GET (2,804-character URL) JSON HTTP 200, 946,205 bytes.
300 POST JSON HTTP 200, 946,205 bytes — byte-identical.
2,000 GET (17,999-character id string) JSON HTTP 414 URI Too Long.
2,000 POST JSON HTTP 200 with error body: Maximum number of UIDs is 500 for JSON format output.
2,000 POST XML HTTP 200, 6,436,193 bytes, 2,000 <DocSum> records.
500 / 501 POST JSON 500 succeeded; 501 rejected with the same error.

Three findings worth separating out, all observed only:

  1. The 200-UID guidance is about URL length, not a server limit. 300 UIDs on a GET worked perfectly and returned exactly what POST returned. The real GET cliff sits somewhere between a 2.8 KB and an 18 KB URL.
  2. ESummary JSON has a hard 500-UID cap, and it is not in the parameter documentation. The boundary is exact: 500 returns, 501 errors.
  3. That cap is format-specific. The identical 2,000-UID POST succeeded with retmode=xml. If you need more than 500 summaries per call you must parse XML; if you want JSON, batch at 500.

Since 500 is comfortably below the URL-length cliff either way, batch at 500 and POST is the setting that satisfies all three constraints at once.

Rate limits: what is documented, and what actually happens

Documented. NCBI’s usage policy is explicit: without an API key, "post no more than three URL requests per second"; with one, "a site can post up to 10 requests per second by default." It also asks that you "limit large jobs to either weekends or between 9:00 PM and 5:00 AM Eastern time during weekdays," and warns that "requests from any IP that lack registered values for tool and email and that violate the above usage policies may be blocked."

Measured, unauthenticated, 26 August 2026. Ten identical ESearch requests fired concurrently, completing in 460 ms total:

  • 3 returned HTTP 200.
  • 7 returned HTTP 429, with the body:
    {"error":"API rate limit exceeded","api-key":"<our egress IP>","count":"4","limit":"3"}

The limit is enforced exactly as documented — limit: 3 — and three of ten got through, which is what a one-second window with three slots looks like. Two consequences follow that the policy text does not spell out:

  1. Without a key, the throttle is keyed to your source IP. The api-key field in the 429 body contained our egress address. Behind an institutional NAT or a shared VPN exit, everyone in the building shares the same three requests per second, including whatever other tools your colleagues are running. This alone is a good reason for a research office to register a key even for modest volumes.
  2. A 429 is JSON, but it is not the JSON your parser expects. There is no esearchresult object. Code that reaches straight for json["esearchresult"]["count"] raises; code that uses a defensive .get() chain records a zero and moves on. A throttled run can therefore report a department with no publications rather than an error.

Measured, paced. The same nine requests sent sequentially with a 0.34-second sleep between them: 9 of 9 returned HTTP 200, total elapsed 6,785 ms. Sequential clients get most of their spacing free from round-trip latency — the effective rate there was about 1.3 requests per second. Concurrency is the thing that breaks the limit, not volume. A worker pool of six against a 3/second cap will spend most of its time being refused.

Getting and using an API key

An API key is issued from the Account Settings page of an NCBI account and is free. Once you have one, append &api_key=YOUR_KEY to every request; the ceiling rises to the documented 10 per second. Keep it out of the repository and out of logged URLs — it identifies your institution’s quota, and the 429 body demonstrates that NCBI echoes the throttling key back to the client.

Send tool and email on every request whether or not you have a key. They cost nothing, and they are the difference between NCBI emailing you about a runaway script and NCBI blocking your address range.

Why an author search is not a publication set

This is where research offices lose the most time, and no amount of API skill fixes it, because the problem is in the data rather than in the interface. PubMed’s author field indexes the name string as printed. It does not identify people.

Measured, 26 August 2026:

Query Records Translation
Wang Y[au] 370,765 y, wang[Author] OR wang y[Author]
Zhang C[au] 92,805 zhang c[Author]
Smith J[au] 34,878 smith j[Author]
Smith J[1au] 8,970 smith j[Author - First]
Smith John A[fau] 38 smith, john a[Author]

Those are not the publication counts of five researchers. They are the number of PubMed records on which a particular initialised name string appears. Any script that treats LastName Initial[au] as "this person’s papers" is, for common names, wrong by three or four orders of magnitude — and the failure is not visible in the output, because every record it returns is a real record with the right name on it.

What actually narrows it

Measured, on the same base query:

Refinement Records What it costs you
Smith J[au] 34,878
Smith J[au] AND Oxford[ad] 879 Everything published before the affiliation was recorded, and everything from other posts.
Smith J[au] AND cardiology[ad] 595 Same, plus anything where the department string differs.
Zhang C[au] AND Oxford[ad] 307 Same — and 307 is still not one person.

An affiliation filter is a large improvement and never a solution. PubMed historically recorded only the first author’s affiliation, so back-catalogue coverage is uneven, and an affiliation string is free text that changes when a department renames itself.

ORCID is precise, and its recall is only as good as its deposit

Measured. PubMed indexes ORCID iDs in the [auid] field, and it works exactly as advertised:

  • A real ORCID iD drawn from our own 100-record sample, searched as 0000-0002-XXXX-XXXX[auid] (redacted here), returned 2 records, translated as "0000-0002-XXXX-XXXX"[Author - Identifier]. That author’s printed name string, Zhang C, returns 92,805.
  • The tag is optional for this one. Searching the bare iD with no tag also returned 2, translated as [All Fields], because an ORCID iD is a distinctive enough string to match nothing else. And [orcid] — not a real tag — also returned 2, silently discarded, falling back to the same all-fields match. It gave the right answer for the wrong reason, which is the most dangerous kind of passing test.

Measured coverage. Across 100 consecutive records from the Oxford 2025–2026 set, 1,198 author entries carried 300 ORCID identifiers — 25.0%. That is a single affiliation slice and not a global figure, but the shape of the finding is the point: three-quarters of author entries in a recent, well-resourced, research-intensive sample have no ORCID attached in PubMed. An [auid] search is therefore precise but incomplete. It will not return a paper the author never linked, and it cannot return one published before they registered.

The practical consequence for a research office: use [auid] as a high-confidence seed, not as the whole search. Union it with a name-plus-affiliation search, then have the researcher confirm the difference. CASRAI covers the underlying problem in depth in author name disambiguation, ORCID and algorithmic matching in a CRIS, and the mechanics of getting an iD populated in the first place in how to add publications to ORCID. If the pull has to reconcile several databases rather than just PubMed, compiling one complete publication list across PubMed, Scopus, Web of Science and Google Scholar is the wider procedure this API call sits inside.

ELink, and the merge that loses your attribution

ELink answers "what is connected to this?" — PubMed to PMC full text, PubMed to related articles, PubMed to the citing literature. Measured, for a single PMID, elink.fcgi?dbfrom=pubmed&db=pubmed&id=22745249 returned eight link sets:

linkname Links What it is
pubmed_pubmed 100 Related-articles neighbours, capped at 100.
pubmed_pubmed_citedin 7,767 Records that cite this one — see the caveat below.
pubmed_pubmed_refs 35 This record’s own reference list.
pubmed_pubmed_alsoviewed 3,349 Usage-derived, not citation-derived.
pubmed_pubmed_reviews 12 Related reviews.
pubmed_pubmed_five, _combined, _reviews_five 5, 5, 5 Pre-trimmed "top five" convenience sets.

The trap: comma-joined IDs are merged into one set

Measured. Two PMIDs, two ways of sending them:

Request Link sets returned
&id=22745249,31978945 1ids: ["22745249","31978945"], 21,948 links, deduplicated union. Attribution lost.
&id=22745249&id=31978945 2 — 7,767 links and 14,196 links, correctly separated.

This is the single most common ELink mistake. If you need per-paper results — and for a publication list you always do — repeat the id parameter. Comma-joining is for when you genuinely want the union.

Observed: the same merge happens when you drive ELink from a History handle, and a further cap appears with cmd=neighbor_score. Five PMIDs posted via EPost and linked with linkname=pubmed_pubmed_citedin returned one merged set of 30,738 links with no cmd, but only 1,000 with cmd=neighbor_score. If a scored ELink result lands on exactly 1,000, it is truncated, not complete.

E-utilities is a retrieval API. It is not a citation-count source.

Because pubmed_pubmed_citedin returns a number, it gets used as one, and it should not be. That link set is built from reference lists deposited in PubMed Central. It therefore covers the subset of the literature whose publishers deposit structured references into PMC — not the literature.

Measured, 26 August 2026, against OpenAlex for the same two papers:

Record ELink citedin OpenAlex cited_by_count Ratio
PMID 22745249 (Science, 2012) 7,767 17,562 2.26×
PMID 31978945 (N Engl J Med, 2020) 14,196 30,490 2.15×

Two independent papers, two decades apart, both showing E-utilities capturing well under half. The reference direction is affected too: pubmed_pubmed_refs returned 35 references for PMID 22745249 where OpenAlex recorded 48 referenced works.

The ratio itself is not the finding — it will differ by field, era and publisher, and the two sources count different things (OpenAlex includes preprints, book chapters and non-indexed venues that PubMed does not carry at all). The finding is that the gap is large, systematic and invisible from inside E-utilities. Use citedin for what it is genuinely good at: finding the citing papers so a human can read them. If a number is going into a report, a promotion case or a bibliometric indicator, it has to come from a source that is trying to count citations — OpenAlex, Scopus, Web of Science or Dimensions — and the source has to be named alongside it. CASRAI’s Semantic Scholar Academic Graph API guide and OpenAlex API guide cover the free options and their own rate limits.

The same applies in the other direction: E-utilities will not tell you a record’s open-access status. That is Unpaywall’s job, keyed on the DOI you pulled out of ESummary’s articleids, and CASRAI’s guide to finding and resolving a DOI covers that lookup.

A checklist for a scripted PubMed pull

  1. Send tool and email on every request. Register an API key and send api_key too.
  2. Never assert on the HTTP status alone. Parse the body; check for ERROR and error keys before reading count. 429 is the only error that arrives as a status code.
  3. Assert on querytranslation. Fail the run if the field name you intended is not in it. This is the only defence against a silently discarded tag.
  4. Run retmax=0 first and read count. If it is 9,999 or more, slice the query by date until every slice is under the ceiling.
  5. Use usehistory=y and page with retstart/retmax. It saves URL length; it does not raise the ceiling.
  6. Batch at 500 and use POST. ESummary JSON refuses 501; GET refuses an 18 KB URL.
  7. Go sequential, not concurrent. Round-trip latency does most of the pacing for you; a worker pool does not.
  8. Repeat &id= for ELink when you need per-record results.
  9. Do not report citedin as a citation count.
  10. Run large jobs off-peak — weekends, or 9:00 PM to 5:00 AM US Eastern on weekdays — and cache what you retrieve so you are not re-pulling the same records next quarter.

This page sits in CASRAI’s research tools cluster alongside the interactive side of the same database: the Advanced Search Builder for building the query, and MeSH mechanics for making it recall what you meant.

Frequently asked questions

Do I need an API key to use the PubMed E-utilities API?

No. E-utilities works unauthenticated at NCBI’s documented three requests per second. A free key raises that to ten per second. Measured 26 August 2026: ten concurrent unauthenticated requests produced three HTTP 200s and seven HTTP 429s, with the throttle body reporting "limit":"3" and identifying our egress IP address as the throttling key. That last detail matters in an institution — without a key, everyone behind the same NAT shares those three slots.

What is the maximum number of records I can retrieve from PubMed via E-utilities?

9,999 per query. NCBI’s parameter documentation says 10,000, but measured 26 August 2026, retstart=9999 returned 'retstart' cannot be larger than 9998, and a request for retmax=100000 was silently clamped to 9,999 UIDs with no warning. Using usehistory/WebEnv does not lift the ceiling — EFetch against a history handle raised the identical error at retstart=10000. To go further, partition the query by publication date, use NCBI’s EDirect command-line client, or work from the annual PubMed baseline files.

Does usehistory and WebEnv let me download a set larger than 10,000?

No. The History server means you do not have to send thousands of PMIDs back in a URL, which is a genuine benefit for a 9,000-record pull. It does not change the retrieval ceiling. Measured 26 August 2026 against a 16,896-record history set: retstart=9990&retmax=10 returned 9 records, and retstart=10000 returned an error.

Why does my E-utilities search return more results than the same search in PubMed?

Most often because a field tag was silently discarded. Measured 26 August 2026: crispr[abstract] and crispr[zzz] both returned 70,286 — identical to an untagged crispr — against 64,360 for the valid crispr[tiab]. The API’s errorlist.fieldsnotfound stayed empty in both cases, and the web interface’s red warning banner does not exist over HTTP. Read querytranslation in the response: if it ends in [All Fields] rather than the field you asked for, your tag was dropped.

How do I check that my PubMed field tag is valid before running a query?

Do not rely on an error, because there will not be one. Run the query and assert on esearchresult.querytranslation — a surviving tag appears there as its expanded name, such as "crispr"[Title/Abstract] or "Oxford"[Affiliation]. einfo.fcgi?db=pubmed&retmode=json lists the 48 indexed fields with those expanded fullname values, which is what you assert against. Note that EInfo’s short names are internal index names and are not a one-to-one list of PubMed’s bracket tags.

Why did my E-utilities request return HTTP 200 with an error inside it?

Because that is how E-utilities reports most application-level errors. Measured 26 August 2026: an invalid db value, an oversized ESummary batch, and an out-of-range retstart all returned HTTP 200 with an error string in the body and no usable result object. Only rate limiting returns a real status code (429). Parse the body before trusting the response.

Can I use an author search in PubMed to build a researcher’s publication list?

Not reliably. The [au] field indexes the printed name string, not a person. Measured 26 August 2026: Wang Y[au] returned 370,765 records, Zhang C[au] 92,805, and Smith J[au] 34,878. Adding an affiliation helps a great deal and does not solve it — Zhang C[au] AND Oxford[ad] still returned 307. Use [auid] with an ORCID iD as a high-confidence core, union it with a name-plus-affiliation search, and have the researcher confirm the difference.

How complete is ORCID coverage in PubMed?

Partial, and it varies. Measured 26 August 2026 across 100 consecutive records from a University of Oxford 2025–2026 affiliation search: 1,198 author entries carried 300 ORCID identifiers, or 25.0%. That is one recent, research-intensive slice rather than a global rate, but it makes the practical point — an [auid] search is precise and will silently miss any work the author has not linked, plus everything published before they registered.

Does E-utilities give me citation counts?

No, and the number it does return should not be used as one. pubmed_pubmed_citedin is built from reference lists deposited in PubMed Central, so it covers only the depositing subset of the literature. Measured 26 August 2026: PMID 22745249 returned 7,767 citedin links against 17,562 in OpenAlex; PMID 31978945 returned 14,196 against 30,490. Use citedin to find citing papers to read, and a source that is actually trying to count citations for anything that goes in a report.

Why does ELink return one merged result for several PMIDs?

Because comma-joined UIDs are treated as one input set. Measured 26 August 2026: &id=22745249,31978945 returned a single link set of 21,948 deduplicated links, while repeating the parameter as &id=22745249&id=31978945 returned two link sets of 7,767 and 14,196. Repeat the parameter whenever you need per-record attribution.

How many UIDs can I send in one E-utilities request?

Measured 26 August 2026 against ESummary: 500 in JSON, exactly — 501 returned Maximum number of UIDs is 500 for JSON format output, which is not stated in the parameter documentation. The same 2,000-UID POST succeeded with retmode=xml, returning 2,000 DocSum records, so the cap is format-specific. On GET, 300 UIDs (a 2,804-character URL) worked and returned bytes identical to POST, while 2,000 UIDs produced HTTP 414. Batching at 500 over POST satisfies every limit at once.

Does EFetch support JSON for PubMed?

No. Measured 26 August 2026: efetch.fcgi?db=pubmed&id=42644503&retmode=json returned HTTP 200 with the body 42644503 — a bare UID, silently substituted for the record you asked for. Use ESummary for JSON, or EFetch with retmode=xml, or rettype=medline&retmode=text for MEDLINE tag format.

When should I run a large E-utilities job?

NCBI asks that you "limit large jobs to either weekends or between 9:00 PM and 5:00 AM Eastern time during weekdays," and warns that requests lacking registered tool and email values that violate the usage policies may be blocked. Send both parameters on every request regardless of size — they are what allow NCBI to contact you rather than block your address range.

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →

Regulatory Radar

Stop finding out after the fact

$29/month, cancel anytime. Daily digest updates from our analysis, a dashboard holding the same items, and a cited assistant for everything they raise.

  • Federal Register, Federal Register+, Grants.gov, Regulations.gov, NSF News, UKRI, plus CASRAI’s own published content.
  • 44,322 indexed passages, and every answer cites the ones it drew on.