Skip to main content

PyEuropePMC

PyPI version Python versions CI codecov License: MIT MCP server

PyEuropePMC is a Python client for Europe PMC. It searches the literature, downloads open-access full text, and parses JATS XML into metadata, plain text and structured sections. It also ships a command-line tool and an MCP server for AI agents.

✨ Features

  • Europe PMC search with plain queries or a fluent QueryBuilder, pagination, and results as JSON, XML or Dublin Core. Search guide
  • Ten more sources in one search, with duplicates merged: PubMed, arXiv, ClinicalTrials.gov, OpenAlex, Semantic Scholar, CORE, DBLP, DOAJ, HAL and Zenodo. Semantic Scholar needs the semanticscholar extra and CORE an API key. Multi-source search
  • Full text: XML (Europe PMC first, then other open sources), PDF and HTML, plus bulk PDF downloads from the Europe PMC FTP site. Full-text retrieval
  • JATS XML parsing: metadata, authors, tables, figures and references; plain text and Markdown; typed sections for retrieval-augmented generation (RAG); JATS normalization and BioC export. XML parsing, JATS normalization
  • Text-mining annotations for genes, diseases, chemicals and their relationships. Examples
  • Citations and metadata: walk citation graphs, and enrich records from Crossref, Unpaywall, OpenAlex, Semantic Scholar, DataCite, ORCID, ROR and iCite. Citation walking, Enrichment
  • Analysis: pandas DataFrames, citation statistics, duplicate detection, plots, and PRISMA-style search logs for systematic reviews. Analytics, Review tracking
  • RDF knowledge graphs from parsed articles, mapped by the rdf_map.yml that ships with the package; pass RDFMapper(config_path=...) or PipelineConfig(rdf_config_path=...) to use your own. Data models and RDF
  • A command line and an MCP server, described below.

📦 Installation

pip install pyeuropepmc                              # core
pip install "pyeuropepmc[analytics,visualization]"   # add extras (quote the brackets)
pip install "pyeuropepmc[all]"                       # every optional feature

PyEuropePMC supports Python 3.10 to 3.13. The core install covers search, full-text download, XML parsing, RDF, the command line and the MCP server. The extras add:

Extra Installs Adds
analytics pandas, numpy to_dataframe, citation_statistics, quality_metrics, remove_duplicates and CSV export
visualization matplotlib, seaborn, pandas, numpy The plot_* functions and create_summary_dashboard
export pandas, tabulate, xlsxwriter Excel and Markdown-table export (pyeuropepmc.utils.export)
semanticscholar semanticscholar SemanticScholarClient and the semantic_scholar search source
enrichment semanticscholar Semantic Scholar data in PaperEnricher
bibliography bibtexparser BibTeX parsing, validation and RIS/CSL conversion, including the bib_* MCP tools
zotero pyzotero The Zotero client
agentic openai, langchain, langchain-openai, langgraph, jinja2 LLM analysis, the LLM MCP tools and pyeuropepmc claim
ui flask, tornado The web UI (pyeuropepmc claim serve)
signing cryptography Signed search logs for systematic reviews
benchmark huggingface-hub Downloading the published benchmark datasets (pyeuropepmc benchmark download)
rdf nothing Kept so existing installs keep working; the core rdflib writes JSON-LD itself
standard jupyterlab, notebook, ipykernel, ipython, ipywidgets, matplotlib, seaborn, pandas, numpy, tabulate, xlsxwriter, requests-cache, rich Jupyter plus the analytics, plotting and export packages
all all of the above Every optional feature

Upgrading from 1.x? The migration guide lists what moved into extras.

🚀 Quick start

from pyeuropepmc import FullTextClient, FullTextXMLParser, QueryBuilder, SearchClient

# 1. Search Europe PMC for open-access articles ("CRISPR AND OPEN_ACCESS:y")
query = QueryBuilder().keyword("CRISPR").and_().field("open_access", True).build()
with SearchClient() as search:
    hits = search.search_and_parse(query, pageSize=10)
pmcid = next(hit["pmcid"] for hit in hits if hit.get("pmcid"))

# 2. Download the article's JATS XML (raises an error if no source has it)
with FullTextClient() as fulltext:
    xml_path = fulltext.download_xml_by_pmcid(pmcid, output_path=f"articles/{pmcid}.xml")

# 3. Parse it
parser = FullTextXMLParser(xml_path.read_text(encoding="utf-8"))
metadata = parser.extract_metadata()
print(metadata["title"])
print(", ".join(metadata["authors"][:3]))
text = parser.to_plaintext()  # or parser.to_markdown()

# 4. Collect text blocks with their section path, ready to chunk and embed for RAG
chunks = []
for section in parser.get_full_text_sections_structured():
    if section["section_type"] not in ("front", "body"):  # skip back matter and appendices
        continue
    for block in section["content"]:
        if block.get("text"):
            path = section.get("section_path", section["title"])
            chunks.append({"section": path, "type": block["type"], "text": block["text"]})
print(f"{len(chunks)} text blocks")

section_type is front for the title and abstract, then body, back or appendix. Each block has a type such as paragraph, list, table or figure. Tables and figures carry label and caption, and tables also carry rows. The XML parsing guide covers the other extractors.

🔒 Safe XML parsing

PyEuropePMC parses every XML document it reads with defusedxml: full-text articles, Europe PMC search results, arXiv and PubMed responses, and local files. A DOCTYPE declaration is accepted, but a document that declares entities, internal or external, is refused rather than expanded; FullTextXMLParser raises ParsingError. lxml is not used and does not need to be installed.

💻 Command line

Command What it does
pyeuropepmc unified_search search several sources with deduplication, or compare-sources to see each source's results
pyeuropepmc normalize Turn JATS XML into clean text (text), sections (sections) or BioC JSON (bioc); classify a heading; batch a directory
pyeuropepmc benchmark Score and profile the XML parser on a local folder or a published dataset
pyeuropepmc claim Check the claims in a text against Europe PMC literature with LLM agents (needs the agentic extra and an API key)
pyeuropepmc unified_search search "CRISPR base editing" --limit 10 --output results.json
pyeuropepmc benchmark list-datasets

unified_search search queries Europe PMC, PubMed and arXiv unless you pass --source. Add --help to any command for its options.

🤖 MCP server

pyeuropepmc-mcp serves 24 tools over the Model Context Protocol: multi-source search, paper details and citations, citation-graph walking, ClinicalTrials.gov search, a local full-text index, figure extraction, bibliography conversion and LLM-powered analysis. The server is part of the core install. pip install "pyeuropepmc[all]" enables every tool; otherwise the bib_* tools need bibliography, and the LLM tools need agentic plus an OpenAI-compatible API key (see Configuration below).

pyeuropepmc-mcp                                # stdio, for Claude Desktop and similar clients
pyeuropepmc mcp                                # the same server, through the CLI
pyeuropepmc-mcp --transport streamable-http    # HTTP at http://127.0.0.1:8000/mcp

For Claude Desktop and other clients that start the server themselves:

{
  "mcpServers": {
    "pyeuropepmc": {
      "command": "pyeuropepmc-mcp"
    }
  }
}

Without an install, "command": "uvx" with "args": ["pyeuropepmc", "mcp"] does the same; this is what the MCP Registry entry tells clients to run.

The server has no authentication of its own, so keep the HTTP transport on 127.0.0.1 or put an authenticating proxy in front of it. The MCP server guide lists every tool. The server is listed in the MCP Registry as io.github.JonasHeinickeBio/pyeuropepmc.

⚙️ Configuration

Europe PMC needs no API key. Other services read these environment variables. A value passed in code, such as FullTextClient(email=...) or EnrichmentConfig(unpaywall_email=...), takes precedence.

Variable Used for
UNPAYWALL_EMAIL, CROSSREF_EMAIL Contact e-mail for Unpaywall and Crossref. FullTextClient needs one of them for its Unpaywall fallback.
OPENALEX_EMAIL, DATACITE_EMAIL, ROR_EMAIL, ROR_CLIENT_ID The other enrichment sources
SEMANTIC_SCHOLAR_API_KEY Semantic Scholar, with higher rate limits
CORE_API_KEY The core search source
OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL LLM features, with any OpenAI-compatible API; the model defaults to gpt-4o-mini
ZOTERO_API_KEY, ZOTERO_LIBRARY_ID, ZOTERO_LOCAL The Zotero client
PYEUROPEPMC_MCP_TRANSPORT, PYEUROPEPMC_MCP_HOST, PYEUROPEPMC_MCP_PORT, PYEUROPEPMC_MCP_LOG_LEVEL Defaults for the pyeuropepmc-mcp options

The pyeuropepmc command also loads the first .env file it finds in the current directory, one of its parents, or ~/.config/pyeuropepmc/. Variables that are already set keep their values.

📚 Documentation

The guides are in docs/. Start with installation and the quick start, or go straight to the API reference, caching, the example scripts and the changelog.

Parser benchmark. On the 55 JATS articles in benchmark_xmls/xml, the parser's mean composite quality score is 0.998 (measured on 2026-09-15). The benchmarking guide explains the metrics. To reproduce the score from a source checkout, or to score a published dataset:

pyeuropepmc benchmark run local --local-path benchmark_xmls/xml --limit 55
pyeuropepmc benchmark download PLOS_1000   # 1,000 articles, 1.3 GB
pyeuropepmc benchmark run PLOS_1000

The weekly benchmark workflow times the API clients and opens a pull request that refreshes the section below.

📊 Performance

Last updated: 2026-09-14

Metric Value
Benchmarked methods 10
Total requests 224
Mean call time 0.871s
Success rate 100.0%
📈 View full benchmark report

🚀 pyEuropePMC Benchmark Suite

Generated: 2026-09-14 07:45:39

📊 Summary

  • Total Benchmarks: 6
  • Total Methods: 10
  • Successful Runs: 10
  • Failed Runs: 0
  • Cache Enabled: 5

ArticleClient_NoCache

Method Mean Time Std Dev Mean Memory Cache Requests Errors
get_article_details 1.707s 0.212s 0.0MB ❌ 31 0
p50: 1.620s · p95: 2.235s · ops: 0.59/s · runs: 30
get_citations 1.789s 0.318s 0.0MB ❌ 31 0
p50: 1.666s · p95: 2.851s · ops: 0.56/s · runs: 30

ArticleClient_Cached

Method Mean Time Std Dev Mean Memory Cache Requests Errors
get_article_details <1ms <1ms 0.0MB ✅ 1 0
p50: 17µs · p95: 29µs · ops: 53390.19/s · runs: 30
get_citations <1ms <1ms 0.0MB ✅ 1 0
p50: 19µs · p95: 30µs · ops: 48437.33/s · runs: 30

SearchClient_NoCache

Method Mean Time Std Dev Mean Memory Cache Requests Errors
search 2.074s 0.262s 0.1MB ❌ 31 0
p50: 1.964s · p95: 2.673s · ops: 0.48/s · runs: 30
get_hit_count 2.085s 0.562s 0.0MB ❌ 31 0
p50: 1.933s · p95: 3.668s · ops: 0.48/s · runs: 30

SearchClient_Cached

Method Mean Time Std Dev Mean Memory Cache Requests Errors
search <1ms <1ms 0.0MB ✅ 1 0
p50: 109µs · p95: 134µs · ops: 8795.90/s · runs: 30
get_hit_count <1ms <1ms 0.0MB ✅ 1 0
p50: 111µs · p95: 137µs · ops: 8603.62/s · runs: 30

FullTextClient_NoCache

Method Mean Time Std Dev Mean Memory Cache Requests Errors
check_fulltext_availability 1.050s 0.148s 0.1MB ❌ 93 0
p50: 0.998s · p95: 1.388s · ops: 0.95/s · runs: 30

FullTextClient_Cached

Method Mean Time Std Dev Mean Memory Cache Requests Errors
check_fulltext_availability <1ms <1ms 0.0MB ✅ 3 0
p50: 20µs · p95: 21µs · ops: 48496.45/s · runs: 30

🔁 Cache vs No-Cache Comparison

ArticleClient — cached vs no-cache

Method No-Cache Mean Cached Mean Speedup (no/cache)
get_article_details 1.707s <1ms >17074.4x
get_citations 1.789s <1ms >17888.5x

FullTextClient — cached vs no-cache

Method No-Cache Mean Cached Mean Speedup (no/cache)
check_fulltext_availability 1.050s <1ms >10504.7x

SearchClient — cached vs no-cache

Method No-Cache Mean Cached Mean Speedup (no/cache)
get_hit_count 2.085s <1ms 17941.89x
search 2.074s <1ms 18245.92x
  • Average speedup for SearchClient (no-cache / cached): 18093.91x

Notes: Means are computed over measured iterations; '-' indicates missing data. Values like '<1ms' indicate very fast cached responses. Speedups shown as lower-bounds when cached times are too small to measure precisely.

⚙️ How to reproduce

Run the modular benchmark locally and regenerate these artifacts:

pytest tests/benchmark_article_client.py::test_modular_benchmark_system -m benchmark --force-enable-socket --timeout=3600
  • Detailed JSON results: MODULAR_BENCHMARK_RESULTS.json

🤝 Contributing

Contributions are welcome. The development guide covers setup, testing, code quality and the release process. Report bugs and suggest features in the issue tracker.

📝 Citation

If PyEuropePMC supports your research, please cite it and give the version you used (pip show pyeuropepmc prints it):

@software{pyeuropepmc,
  author = {Heinicke, Jonas},
  title  = {{PyEuropePMC}: a Python toolkit for Europe PMC},
  url    = {https://github.com/JonasHeinickeBio/pyEuropePMC}
}

The literature itself comes from Europe PMC; please acknowledge it as your data source.

📄 License

PyEuropePMC is released under the MIT License. Articles you retrieve keep their own licences, which FullTextXMLParser.extract_license() reads from the XML.

Metadata

Release files for pyeuropepmc 3.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyeuropepmc 3.0.0
File Size Uploaded
pyeuropepmc-3.0.0.tar.gz 678.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyeuropepmc 3.0.0
File Interpreter ABI Platform
pyeuropepmc-3.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.5 MB

Release files / pyeuropepmc-3.0.0.tar.gz

Download URL pyeuropepmc-3.0.0.tar.gz
Size 678.8 kB
Tags Source
SHA-256 checksum
How to use checksums
081787a595d009975674640fc56b8dd1a9895423cca3bc6474c511f8b10a6b73
BLAKE2b-256 checksum
How to use checksums
fdc8393ff9e979201d2cbf72502a3f1d84e13a510de675ba98db7275a976f597
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release files / pyeuropepmc-3.0.0-py3-none-any.whl

Download URL pyeuropepmc-3.0.0-py3-none-any.whl
Size 805.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a9dceb3def6be0c6f75203fc924036e07397e01105a986dbf975a23989650cc3
BLAKE2b-256 checksum
How to use checksums
cf69df2c47a2384520ac271bc34656a6be8a64177dedda09b99aa183698b2368
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

3.0.0 This release

2 release files

2.2.1

2 release files

2.2.0

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.0

2 release files

1.17.0

2 release files

1.16.0

2 release files

1.13.0

2 release files

1.12.0

2 release files

1.11.3

2 release files

1.11.2

2 release files

1.11.1

2 release files

1.11.0

2 release files

1.10.1

2 release files

1.10.0

2 release files

1.9.1

2 release files

1.9.0

2 release files

1.8.1

2 release files

1.8.0

2 release files

1.7.0

2 release files

1.6.0

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page