BiomedCore
BiomedCore provides access to 43M+ biomedical citations sourced from PubMed and PubMed Central, enriched with citation counts, journal quality indicators, and structured metadata. It also covers conference abstracts from society meetings, returned alongside journal articles and identified by Conference in publicationTypes. Updated daily.
Endpoints
Search — GET /v1/cores/biomedcore/records
Search across biomedical literature by text, with optional filters.
curl "https://api.amass.tech/api/v1/cores/biomedcore/records?query=CAR-T+therapy&limit=5" \
-H "Authorization: Bearer amass_YOUR_KEY"Parameters:
| Name | Required | Default | Description |
|---|---|---|---|
query | yes | — | Search text (titles, abstracts, fulltext, metadata) |
limit | no | 20 | Results to return (1–300) |
include | no | — | Optional fields to return. Repeat for multiple: fulltext, authorsMetadata, meshIds, substanceIds, referencesTrialCore, references, citedBy |
minPublicationDate | no | — | ISO date, e.g. 2023-01-01 |
maxPublicationDate | no | — | ISO date, e.g. 2026-01-01 |
minLastUpdateDate | no | — | ISO date. Earliest date Amass last wrote the record. Records with no such date are excluded |
maxLastUpdateDate | no | — | ISO date. Latest date Amass last wrote the record |
minCreateDate | no | — | ISO date. When Amass ingested the record, not when it was published. Records with no create date are excluded |
minCitationCount | no | — | Integer (0–100,000) |
minJournalQualityJufo | no | — | 0, 1, 2, or 3 (see Journal Quality Tiers) |
isRetracted | no | — | true or false |
authorOrcids | no | — | Match ANY of these ORCIDs. Accepts bare (0000-0003-1234-5678) or URL form (https://orcid.org/0000-0003-1234-5678); a value that is not an ORCID is a 400, not an empty result. Repeat the param for multiple values. See Author & institution filters. |
authorNames | no | — | Match ANY of these author names. Free-text token match (no prefix expansion). A two-part name must be carried by a single author, in either order; the citation form (Liu DR) additionally matches the record's own LastName Initials value. Repeat the param for multiple values. |
institutionRors | no | — | Match ANY of these ROR identifiers. Accepts bare (03vek6s52) or URL form (https://ror.org/03vek6s52); a value that is not a ROR ID is a 400, not an empty result. Repeat the param for multiple values. |
institutionNames | no | — | Match ANY of these institution names. Free-text token match against both the normalized institution name and the affiliation line printed on the paper. Repeat the param for multiple values. |
Covering a long window
Search returns a ranked top-K, so a wide lastUpdateDate window can truncate silently. Request long spans in slices — minLastUpdateDate=2026-08-01&maxLastUpdateDate=2026-08-07, then the next week — and union the results. For complete coverage with no truncation, use /changes.
Example with filters — highly cited papers in top journals from 2022 onward:
curl "https://api.amass.tech/api/v1/cores/biomedcore/records\
?query=immunotherapy\
&minPublicationDate=2022-01-01\
&minCitationCount=30\
&minJournalQualityJufo=2" \
-H "Authorization: Bearer amass_YOUR_KEY"Author & institution filters
The four filters authorOrcids, authorNames, institutionRors, and institutionNames follow the same encoding and combine with the same rules.
- Within one filter — OR. Repeat the param to match any of the listed values:
?authorOrcids=0000-0001-...&authorOrcids=0000-0002-...returns papers authored by either ORCID. - Across filters — AND. Mixing different filters narrows the result:
?authorNames=Hassabis&institutionNames=DeepMindreturns papers that have a Hassabis author and a DeepMind affiliation (not necessarily the same author/affiliation pair). - ORCIDs and RORs accept bare or URL form.
0000-0002-2401-5691andhttps://orcid.org/0000-0002-2401-5691match the same author;03vek6s52andhttps://ror.org/03vek6s52match the same institution. These two params take identifiers only: a value that is not one — a name, say — returns400 VALIDATION_ERRORrather than an empty page. Names belong inauthorNamesandinstitutionNames. - Institution names match the affiliation line too. Not every record carries a normalized institution name — that comes from an enrichment pass that does not reach every paper, and it is sparser the further back you go.
institutionNamestherefore also matches the raw affiliation text as printed on the paper, so smaller organisations, companies, and older records stay reachable. Matching is still token-based, soMoffittfindsMoffitt Cancer Center. - Author names match in several forms.
Liu DR,David R Liu,LiuandLiu, Davidare all accepted. Any two-part name — the citation form included — is matched against a single author on the paper, in either order and ignoring any middle name or initial between the two parts. SoDavid Liufinds a paper byDavid R. Liu, andLiu DRfinds one byDavid R. Liu, but neither matches a paper whose authors areDavid ChenandAmy Liu. The citation form is additionally matched against the record's ownLastName Initialsvalue. A surname alone is the widest match and the one to fall back to, withqueryfor context when the surname is common.
# Papers by one of two ORCIDs at a specific institution (ROR), since 2022
curl "https://api.amass.tech/api/v1/cores/biomedcore/records\
?query=protein+structure+prediction\
&authorOrcids=0000-0002-2401-5691\
&authorOrcids=0000-0001-9282-0298\
&institutionRors=00971b260\
&minPublicationDate=2022-01-01\
&include=authorsMetadata\
&limit=20" \
-H "Authorization: Bearer amass_YOUR_KEY"Response:
{ "data": [ { "amassId": "AMBC_...", "title": "...", "..." }, ... ] }Get by ID — GET /v1/cores/biomedcore/records/{amassId}
Fetch a single record by its Amass ID.
curl "https://api.amass.tech/api/v1/cores/biomedcore/records/AMBC_abc123\
?include=authorsMetadata" \
-H "Authorization: Bearer amass_YOUR_KEY"Response:
{ "data": { "amassId": "AMBC_...", "title": "...", "..." } }Returns 404 if not found.
Lookup — POST /v1/cores/biomedcore/records/lookup
Convert PMIDs or DOIs to Amass IDs in batch. Each item must contain exactly one identifier — either pmid or doi, not both.
DOI matching is case-insensitive, as the DOI specification requires: 10.1523/JNEUROSCI.3402-08.2008 and 10.1523/jneurosci.3402-08.2008 resolve to the same record. The doi a record returns is the publisher's deposited spelling, which is not always lowercase.
curl -X POST "https://api.amass.tech/api/v1/cores/biomedcore/records/lookup" \
-H "Authorization: Bearer amass_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "pmid": "38123456" },
{ "doi": "10.1038/s41586-024-00001-x" }
]
}'Response:
{
"data": [
{
"input": { "pmid": "38123456" },
"amassIds": ["AMBC_abc123"]
},
{
"input": { "doi": "10.1038/s41586-024-00001-x" },
"error": { "code": "NOT_FOUND", "message": "No matching record found" }
}
]
}Individual items can fail without failing the whole request. Always check each item for an error field.
Changes — GET /v1/cores/biomedcore/changes
A resumable feed of publications that were created, updated, or deleted, for keeping a local copy in sync without re-reading the corpus. Read updateType to tell them apart.
curl "https://api.amass.tech/api/v1/cores/biomedcore/changes\
?since=2026-01-01\
&limit=500" \
-H "Authorization: Bearer amass_YOUR_KEY"Parameters:
| Name | Required | Default | Description |
|---|---|---|---|
since | yes | — | ISO date — start of the change window, inclusive |
limit | no | 100 | Items per page (1–1000) |
cursor | no | — | Opaque cursor from a previous response's nextCursor |
amassId | no | — | Repeat to follow only these records (max 100). Omit for the whole corpus — see Watching a specific set of publications |
Response:
{
"data": [
{
"amassId": "AMBC_abc123",
"updateType": "updated",
"updateDate": "2026-01-14",
"createDate": "2024-03-02"
},
{
"amassId": "AMBC_def456",
"updateType": "deleted",
"updateDate": "2026-01-15",
"createDate": null
}
],
"nextCursor": "eyJ2IjoxLCJ..."
}Response fields:
| Field | Type | Description |
|---|---|---|
amassId | string | The record that changed |
updateType | string | created, updated, or deleted |
updateDate | string | The change that caused this delivery. For a deletion, the date the removal was observed |
createDate | string | null | When the record entered Amass. Always null on a deletion |
nextCursor | string | null | Pass back as cursor. null means caught up through the last fully-ingested day |
The feed returns identifiers and change metadata, not publication content — fetch the records you care about with Get by ID once you know which ones moved.
Paging. Pass the nextCursor from each response back as cursor on the next request. Keep going until nextCursor is null — that is the only signal you are caught up. A page can hold fewer than limit items and still have a cursor: limit bounds the rows the feed scans, not the items it returns, so both a day boundary and a record that changed more than once on one day leave a page short.
Delivery is at-least-once. The same amassId can appear more than once across pages, so apply the feed as an upsert rather than a counter.
created describes the delivery, not the record. It means this is the first time the feed has handed you that publication. One that entered Amass inside your window and then changed again — fulltext arriving, a citation count refreshing — arrives once, as updated. To tell genuinely new publications from long-standing ones, compare createDate against your window rather than branching on updateType.
The most recent day is withheld. A day is only served once ingestion for it has finished, so an in-progress run cannot hand you a half-written day and leave the rest stranded behind your cursor. In practice a change becomes visible about a day after it lands.
Resume from a day or two before your last since, not from "now". The feed serves everything from since up to the last fully-ingested day, so nextCursor: null means you have that — not that you are caught up to the current date. Advancing your next since to today therefore steps over the withheld day and never comes back for it. Store the since you asked for and re-ask from slightly before it: delivery is at-least-once and you upsert by amassId, so the overlap costs a page and nothing else.
There is deliberately no until and no throughDate on this endpoint. Both existed in earlier drafts and both read as a second, competing resume token next to the cursor. If your integration needs an explicit end bound or a server-published watermark, tell us — they are held back pending that demand, not removed on principle.
Watching a specific set of publications
If you track a fixed set of papers rather than a mirror of the corpus — the references behind a living review, say — pass amassId, repeated, up to 100 per request:
curl "https://api.amass.tech/api/v1/cores/biomedcore/changes\
?since=2026-01-01\
&amassId=AMBC_abc123\
&amassId=AMBC_def456" \
-H "Authorization: Bearer amass_YOUR_KEY"Everything else is unchanged — same envelope, same updateType, same cursor, same price — and deletions still arrive, so a withdrawn citation in your set still reaches you. On this Core scoping is also the practical answer to the volume warning above: a watchlist is unaffected by a corpus-wide citation refresh unless one of your records moved.
Repeat the parameter once per paper. A comma-joined list — amassId=AMBC_abc123,AMBC_def456 — is read as one identifier and rejected.
Two consequences worth knowing:
- A scoped page can never hold more than 100 distinct records, so
limitabove 100 does nothing. - A cursor is bound to the
amassIdset it was issued for. Change the set while paging and the cursor is rejected with a 400 — see Changing the watchlist.
Records whose Amass ID is malformed, belongs to another Core, or is sent as an empty value are rejected outright rather than skipped. A silently dropped id would leave you believing a paper is being watched when it is not.
Watching more than 100 publications
Split the watchlist into chunks of 100 and sweep each chunk independently.
Chunk the same way on every run. Each chunk carries its own cursor, and a cursor is bound to the exact set of ids that issued it, so re-partitioning between runs invalidates every cursor at once. Sort by amassId and slice, or derive the chunk from a stable property of the record. Do not chunk by iteration order over a set or hash map.
Chunking stably also makes adding a paper a change to one chunk rather than to all of them.
Changing the watchlist
A cursor is bound to the amassId set that issued it, so changing that set mid-sweep gets you a 400 on the next request.
This matters less than it first appears, because cursors are sweep-local. What you persist between runs is your own since, not the cursor; the next sweep starts from since with no cursor at all. A reference list edited between sweeps therefore costs nothing.
That leaves editing mid-sweep, which you can avoid entirely by paging to nextCursor: null before applying changes — a scoped sweep usually takes one or two requests. If you do have to edit mid-sweep, the recovery depends on which direction it went:
- Removed a paper. Nothing is missing. Restart from the
sinceyou last stored. Re-asking an overlapping window is safe and cheap, because delivery is at-least-once and you upsert byamassIdanyway. - Added a paper. The new paper needs the full window regardless, so resuming would not have helped it. Either restart the sweep, or run the added ids as their own request from whichever date you want their history to begin.
A retraction watch is the natural use: retracted papers stay indexed and arrive as updated with isRetracted: true, so scoping the feed to the papers you cite tells you when your evidence base decays.
Deletions
Deletions arrive on this feed as updateType: "deleted", under the same cursor as every other change.
They are deliberately not a separate feed. A second feed would mean a second cursor over the same records, and two cursors can disagree: a record deleted on one day and re-created on a later one is applied in the wrong order by a consumer that is further along one feed than the other — and nothing would ever re-deliver it. One feed, one cursor, events in date order.
A retraction is not a deletion. Retracted papers stay indexed — they arrive as updateType: "updated" with isRetracted set to true.
The feed reports current state, not a complete event log. A record removed more than once reports only its most recent removal.
Record Schema
Default fields (always returned)
| Field | Type | Description |
|---|---|---|
amassId | string | Unique Amass identifier (AMBC_...) |
pmid | string | null | PubMed ID |
pmcid | string | null | PubMed Central ID |
doi | string | null | Digital Object Identifier, as the publisher deposited it. Lookups match case-insensitively |
url | string | null | Link to the source of this record. The target may change over time; use the identifier fields to build links to specific external systems |
title | string | null | Article title |
abstract | string | null | Article abstract |
authors | string[] | Author names (e.g. ["Smith J", "Doe A"]) |
journal | string | null | Journal name |
issn | string | null | Journal ISSN |
volumeIssue | string | null | Volume and issue |
publicationDate | string | null | ISO date: the electronic publication date when the source record has one, otherwise the journal issue date |
lastUpdateDate | string | null | ISO date Amass last wrote the record |
createDate | string | null | ISO date the record entered Amass. Null for records ingested before Amass tracked this |
publicationTypes | string[] | NLM MeSH publication types, e.g. ["Journal Article", "Review"]. Records sourced from a conference abstract also carry the Amass-assigned value Conference, appended last (see Conference abstracts) |
language | string | null | Language code (e.g. eng) |
citationCount | number | null | Citation count |
journalQualityJufo | number | null | JuFo journal quality level (0–3, see below) |
meshTerms | string[] | MeSH terms |
keywords | string[] | Author keywords |
substances | string[] | Chemical substances |
hasFulltext | boolean | null | Whether full text is available |
isRetracted | boolean | null | Whether article is retracted |
Optional fields
Request these with the include parameter. Repeat for multiple: ?include=fulltext&include=authorsMetadata
| Field | Include value | Type | Description |
|---|---|---|---|
fulltext | fulltext | string | null | Full article text. Large — only request when needed. |
authorsMetadata | authorsMetadata | object[] | Structured author details (see below) |
meshIds | meshIds | string[] | MeSH descriptor identifiers |
substanceIds | substanceIds | string[] | Chemical substance identifiers |
referencesTrialCore | referencesTrialCore | string[] | Cross-core link → TrialCore. Amass IDs of TrialCore records (clinical trials) referenced by this publication |
references | references | string[] | Intra-core link within BiomedCore. Amass IDs of other BiomedCore records (publications) referenced by this publication |
citedBy | citedBy | string[] | Intra-core link within BiomedCore. Amass IDs of other BiomedCore records (publications) that cite this publication |
Reference field semantics
╔══════════════════ BiomedCore (publications) ══════════════════╗
║ ║
║ AMBC_aaa ─cites─┐ ┌─► AMBC_p001 ║
║ │ ├─► AMBC_p002 ║
║ │ ├─► AMBC_p003 ║
║ │ ┌──────────┐ │ ║
║ ├──►│ AMBC_X │────────┤ ⋮ ║
║ │ └──────────┘ │ ║
║ │ ├─► AMBC_p050 ║
║ │ ├─► AMBC_p051 ║
║ AMBC_bbb ─cites─┘ └─► AMBC_p052 ║
║ ║
║ AMBC_X.citedBy = [AMBC_aaa, AMBC_bbb] ← 2 IDs ║
║ AMBC_X.references = [AMBC_p001, …, AMBC_p052] ← 52 IDs ║
║ ║
║ ── intra-core: arrows stay within BiomedCore ── ║
║ ║
╚════════════════════════════════════════════════════════════════╝
│
│ AMBC_X.referencesTrialCore = [AMTC_xxx]
│ ── cross-core link to TrialCore ──
▼
╔══════════════════ TrialCore (clinical trials) ════════════════╗
║ ║
║ AMTC_xxx ║
║ ║
╚════════════════════════════════════════════════════════════════╝- Intra-core arrows stay inside BiomedCore — target IDs start with
AMBC_. Both fan-in (citedBy) and fan-out (references) live in the same Core. - Cross-core arrow leaves BiomedCore for TrialCore — target IDs start with
AMTC_. OnlyreferencesTrialCorecrosses the Core boundary.
authorsMetadata shape
{
"name": "Smith J",
"nameRaw": "John Smith",
"orcid": "0000-0001-2345-6789",
"position": 0,
"affiliations": [
{
"name": "Harvard Medical School",
"nameRaw": "Harvard Medical School, Boston, MA, USA",
"ror": "03vek6s52",
"countryCode": "US"
}
]
}Conference abstracts
BiomedCore includes abstracts presented at conferences alongside journal
articles. They are searched and returned the same way, and are identified by the
value Conference in publicationTypes:
{
"title": "Pembrolizumab plus chemotherapy in advanced NSCLC",
"journal": "Journal of Clinical Oncology",
"publicationTypes": ["Conference"]
}Conference is not a MeSH publication type. It is assigned by Amass, and it is
deliberately outside NLM's vocabulary so the two cannot be confused. It is always
appended last, and NLM's values are never removed or reordered.
Some conference abstracts are also indexed in PubMed under their own PMID. Those
are returned as a single record rather than two: the article's PubMed metadata
(PMID, MeSH terms, NLM publication types) is kept, and Conference is appended
to it. So a record carrying both Journal Article and Conference is one
publication known through both routes, not a duplicate.
On those records Conference is appended only when PubMed's own metadata agrees
— an NLM conference type such as Congress, or a supplement Amass has attributed
to a named conference — and never to an item a conference proceeding cannot be,
such as an Editorial. Where PubMed describes the record as an ordinary article
and nothing corroborates the conference, it is returned exactly as PubMed
describes it. A record with a PMID and no Conference is therefore not proof
that the work was never presented at one.
journalQualityJufo on a conference abstract is the rating of the journal that
published the supplement it appeared in — ESMO's abstracts are in Annals of
Oncology, ASCO's in the Journal of Clinical Oncology — so
minJournalQualityJufo applies to them as it does to an
article. Use publicationTypes to tell the two apart.
Journal Quality Tiers
Journal quality is based on Publication Forum (JuFo), an openly published classification of publication channels.
| Level | Meaning |
|---|---|
| 3 | Highest quality — represents the highest level in the discipline with extremely consistent impact |
| 2 | Domain leading — limited number of journals, conferences, and publishers of the highest level |
| 1 | Peer reviewed — peer-reviewed channels specialised in scientific research with an expert editorial board |
| 0 | Low quality — evaluated but does not meet all Level 1 criteria |
| null | Not evaluated — publication channel has not been evaluated |
See Also
- TrialCore — Clinical trial records
- DrugCore — Drug and molecule records
- RegulatoryCore — FDA & EMA drug authorizations
- GeneCore — Gene records
- API Workflows — See BiomedCore in action: literature reviews, scientific maturity assessment, investigator discovery
- Overview — Errors and rate limits
- Quickstart — Getting started with examples