Europe PMC Scraper: Life-Science Literature & Full Text
Pricing
from $0.37 / 1,000 article scrapeds
Europe PMC Scraper: Life-Science Literature & Full Text
Scrape Europe PMC: citations, abstracts and full text where open access, plus MeSH terms, grants and cited-by counts. Broader coverage than PubMed for European research.
Pricing
from $0.37 / 1,000 article scrapeds
Rating
0.0
(0)
Developer
Arman Hossain
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
21 days ago
Last modified
Categories
Share

Europe PMC Scraper pulls biomedical literature out of Europe PMC, titles, authors, journals, abstracts, DOIs, PMIDs and PMCIDs, MeSH terms, funder grants, cited-by counts, and the complete open-access full text as clean plain text.
Europe PMC indexes everything PubMed does and more: preprints, patents, theses, agency reports, and the European funder record that PubMed does not carry. For anyone building a RAG corpus or running a systematic review. It is the better starting point, and unlike PubMed's E-utilities. It has no key, no email-in-the-URL requirement and no 3-requests-per-second ceiling.
Agent skill: SKILL.md
https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/europe-pmc-scraper.md
What you get
| Output field | Meaning |
|---|---|
id, source | Europe PMC record ID and which index it came from (MED, PMC, PPR for preprints, PAT for patents…) |
pmid, pmcid, doi | The three identifiers you join on. Any may be null, see the FAQ |
title, authorString | Article title (markup stripped) and the full author list as one string |
journalTitle, journalIssn | Publishing journal |
pubYear, firstPublicationDate | Publication year and exact first-publication date |
abstractText | Full abstract as plain text, markup stripped (core only) |
isOpenAccess, inEPMC, hasPdf | Real booleans, converted from Europe PMC's "Y"/"N" strings |
citedByCount | Citations recorded by Europe PMC |
grantsList | Funder records: grantId, agency, acronym, the field PubMed does not give you |
meshTerms | MeSH descriptor names, e.g. ["Humans","Cardiovascular Diseases"] |
fullTextUrl | Best available link, open access preferred, DOI as fallback |
fullTextUrls | Every link Europe PMC knows, with its availability |
europePmcUrl | Canonical article page |
fullText, fullTextChars | Only when includeFullText is on, the article body as clean plain text |
query, scrapedAt | Which query produced the row, and the run timestamp |
A RUN_SUMMARY record in the key-value store holds per-run counts, the filters used, any query that failed, and how many articles actually yielded full text.
Common use cases
1. Build an open-access biomedical corpus for RAG. Open access on, full text on, core for the metadata that makes chunks citable.
{"searchQueries": ["CRISPR gene editing"],"resultType": "core","openAccessOnly": true,"includeFullText": true,"maxResults": 500}
2. Grant and funding analysis. grantsList is the differentiator here, search by agency and get every funded paper with its grant IDs.
{"searchQueries": ["GRANT_AGENCY:\"Wellcome Trust\" AND PUB_YEAR:2025","GRANT_AGENCY:\"Gates Foundation\" AND PUB_YEAR:2025"],"resultType": "core","maxResults": 2000}
3. Systematic reviews with full-text access. MeSH-scoped searching plus full text means screening on the body, not just the abstract.
{"searchQueries": ["MESH:\"Neoplasms\" AND MESH:\"Immunotherapy\" AND PUB_YEAR:[2023 TO 2026]"],"resultType": "core","openAccessOnly": true,"includeFullText": true,"maxResults": 1000}
Quick start
Simplest possible run:
{"searchQueries": ["long covid"]}
Fast, lightweight sweep for change detection:
{"searchQueries": ["long covid"],"resultType": "lite","maxResults": 5000}
Input
| Field | Type | Default | Notes |
|---|---|---|---|
searchQueries | array | - | Required. One search per entry. Plain words or Europe PMC field syntax. Each is paginated independently. |
resultType | string | core | core for abstracts, MeSH and grants; lite for identifiers and citation basics only. Both carry the PMCID. |
openAccessOnly | boolean | false | Appends AND (OPEN_ACCESS:y) to every query. |
includeFullText | boolean | false | Downloads the JATS full text for every OA record with a PMCID and adds it as plain text. |
maxResults | integer | 200 | Total cap across all queries. 0 = no cap. |
Which combinations make sense. includeFullText works with either resultType, lite returns the PMCID too, and pages 1,000 records at a time instead of 100, so it is the cheaper way to build a full-text corpus when you do not need the abstract and MeSH terms. includeFullText really wants openAccessOnly: true though: full text exists only for the OA subset, so without the filter most articles come back with fullText: null while you still pay for the row. Use lite when you are only diffing identifiers on a schedule. It is several times smaller on the wire.
Europe PMC query syntax
| Query | Finds |
|---|---|
long covid | Free text across everything |
TITLE:"gene therapy" | Title match |
AUTH:"Smith J" | Author |
JOURNAL:"Nature" | Journal |
PUB_YEAR:2025 or PUB_YEAR:[2023 TO 2026] | Year, exact or range |
MESH:"Neoplasms" | MeSH term |
GRANT_AGENCY:"Wellcome Trust" | Funder |
DOI:"10.1038/s41586-024-00001-0" | Exact DOI lookup |
SRC:PPR | Preprints only |
OPEN_ACCESS:y | Open-access subset (or just set openAccessOnly) |
HAS_FT:y | Records with full text in Europe PMC |
Combine with AND, OR, NOT and parentheses.
Output example
{"query": "long covid","id": "42328279","source": "MED","pmid": "42328279","pmcid": "PMC13279208","doi": "10.1016/j.jhlto.2026.100600","title": "Long COVID sequelae in heart transplant recipients.","authorString": "Lees CR, Morad T, Webb M, Sim MS, Hsu JJ.","journalTitle": "JHLT open","journalIssn": "2950-1334","pubYear": "2026","firstPublicationDate": "2026-05-26","abstractText": "Post acute sequelae of SARS-CoV-2 infection (also known as Long COVID) have been defined as symptoms that persist after 3 months …","isOpenAccess": true,"inEPMC": true,"hasPdf": false,"citedByCount": 0,"grantsList": [{ "grantId": "226350/Z/22/Z", "agency": "Wellcome Trust", "acronym": "WT" }],"meshTerms": ["Humans", "Post-Acute COVID-19 Syndrome", "Heart Transplantation"],"fullTextUrl": "https://europepmc.org/articles/PMC13279208","fullTextUrls": [{ "site": "DOI", "url": "https://doi.org/10.1016/j.jhlto.2026.100600", "availability": "Subscription required", "documentStyle": "doi" },{ "site": "Europe_PMC", "url": "https://europepmc.org/articles/PMC13279208", "availability": "Open access", "documentStyle": "html" }],"europePmcUrl": "https://europepmc.org/article/MED/42328279","fullText": "The COVID-19 pandemic has resulted in nearly 800 million confirmed cases and over 7 million deaths globally since its emergence in late 2019. …","fullTextChars": 20665,"scrapedAt": "2026-08-06T11:48:44.221Z"}
RUN_SUMMARY:
{"resultType": "core","queriesRequested": 1,"queriesFailed": 0,"failures": [],"articlesSaved": 500,"fullTextFetched": 431,"filters": {"searchQueries": ["CRISPR gene editing"],"resultType": "core","openAccessOnly": true,"includeFullText": true,"maxResults": 500},"finishedAt": "2026-08-06T12:04:11.903Z"}
Limits and behaviour
- Pagination is cursor-based, and that matters. Europe PMC's page-number pagination breaks past 1,000 results;
cursorMarkdoes not. This Actor uses the cursor exclusively, carryingnextCursorMarkforward and stopping when the API hands back the cursor it was given. Verified live walking six pages deep past the 1,000-record mark with no gap or repeat. There is no depth ceiling, somaxResults: 0on a broad query really will try to fetch all 1,063,524 hits, set a cap you mean. - Full text is the OA subset only. Roughly a fifth of Europe PMC has open-access full text. Records without it get
fullText: nullandfullTextChars: 0; a missing full text is not counted as a failure, because it is the normal case.RUN_SUMMARY.fullTextFetchedtells you how many actually came back with text. - Full text is cleaned, not raw. Europe PMC serves JATS XML. This Actor extracts
<body>, drops references, figures, tables and formulae, they become unreadable once markup is stripped, then decodes entities and normalises whitespace. Text is capped at 500,000 characters per article, which no real article reaches. - Titles and abstracts are stripped of markup. Europe PMC returns structured abstracts headed with
<h4>Background</h4>and chemistry titles containing<sub>/<sup>. Block tags become a space so sections do not fuse together, inline tags are removed outright soLaMn<sub>1</sub>O<sub>3</sub>staysLaMn1O3, and what you get is plain text a RAG chunker can ingest. - Entities are decoded twice. Europe PMC double-encodes some free-text fields, one grant agency arrives as
US President&apos;s Malaria Initiative, so decoding runs twice and you getUS President's Malaria Initiative. isOpenAccessis a real boolean. Europe PMC returns the string"Y". This Actor convertsisOpenAccess,inEPMCandhasPdftotrue/falseso you can filter without string comparison.- A rejected query doesn't kill the run. Europe PMC reports bad queries as HTTP 200 with an
errCodebody rather than an error status; the Actor detects that, records the query inRUN_SUMMARY.failures, and continues. It only errors out if every query fails. - Partial results are kept. If a query fails on its fourth page, the 300 articles from pages 1-3 stay in the dataset and the failure is still reported.
- Transient errors are retried. 429 and 5xx get three attempts with linear backoff.
- Public data only. No authentication, no personal data, no access-control bypass.
API example
curl -X POST "https://api.apify.com/v2/acts/arman-bd~europe-pmc-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"searchQueries": ["GRANT_AGENCY:\"Wellcome Trust\" AND PUB_YEAR:2025"],"resultType": "core","maxResults": 100}'
JavaScript example
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_TOKEN' });const run = await client.actor('arman-bd/europe-pmc-scraper').call({searchQueries: ['MESH:"Neoplasms" AND MESH:"Immunotherapy"'],resultType: 'core',openAccessOnly: true,includeFullText: true,maxResults: 200,});const { items } = await client.dataset(run.defaultDatasetId).listItems();const withText = items.filter((a) => a.fullTextChars > 0);console.log(`${withText.length}/${items.length} articles have full text`);for (const a of withText.slice(0, 5)) {console.log(`${a.pmcid}, ${a.title} (${a.fullTextChars} chars)`);}
FAQ
Do I need a proxy? No. Proxy configuration is not required to run this Actor.
Do I need an account or API key? No, unlike PubMed's E-utilities, Europe PMC asks for neither.
What happens if Europe PMC is unavailable? 429s and 5xx errors are retried with backoff. If a query still fails. It is recorded in RUN_SUMMARY.failures and the run continues with the remaining queries.
Can I schedule it? Yes. firstPublicationDate plus a diff on id gives a clean feed of what is new since the last run. resultType: "lite" makes those sweeps much cheaper to transfer.
Why is pmcid null on some records? Only articles deposited in PubMed Central have one. Subscription-only articles are indexed with a PMID and DOI but no PMCID, and therefore no full text.
Why is abstractText null? Either you used resultType: "lite", which omits it, or the record has no abstract (common for preprints, patents and correspondence).
Why did includeFullText give me mostly nulls? You almost certainly left openAccessOnly off. Full text exists only for the OA subset, turn the filter on and the hit rate jumps.
How does this differ from PubMed? Europe PMC is a superset: it mirrors all of MEDLINE and adds preprints, patents, agency reports, theses and the European funder record. grantsList and open-access full text are the two things you cannot get this cleanly from PubMed.
Can I get more than 1,000 results? Yes, that is exactly what the cursor pagination is for. Raise maxResults and it keeps going.
Can I integrate it with something else? Yes, Apify API, client libraries, webhooks, scheduled runs, dataset exports (JSON/CSV/Excel) or MCP. Output is structured JSON.