
Literature Search Europepmc
- 1.5k installs
- 2.6k repo stars
- Updated July 7, 2026
- google-deepmind/science-skills
literature-search-europepmc provides documented workflows for >
About
The literature-search-europepmc skill > # Europe PMC Database A skill for searching, downloading, and exploring open-access papers from [Europe PMC](https://europepmc.org/) - a comprehensive, free life-science literature database with over 43 million abstracts and 9 million full-text articles. **`uv`**: Read the `uv` skill and follow its Setup instructions to ensure `uv` is installed and on PATH. **User Notification**: If LICENSE_NOTIFICATION.txt does not already exist in this skill directory then (1) prominently notify the user to check the terms at https://europepmc.org/ and to always check the license of the papers retrieved by the skill for any restrictions, then (2) create the file recording the notification text and timestamp. ## Core Rules - **Open Access Only**: This skill exclusively searches open-access content. The script automatically appends `OPEN_ACCESS:y` to every search query. Do NOT remove or override this filter. - **NEVER run python3 or python3 -c directly**: the system Python does not necessarily have all key dependencies.
- **`uv`**: Read the `uv` skill and follow its Setup instructions to ensure
- **User Notification**: If LICENSE_NOTIFICATION.txt does not already exist in
- **Open Access Only**: This skill exclusively searches open-access content.
- **NEVER run python3 or python3 -c directly**: the system Python does not
- **Use the Wrapper**: ALWAYS use the provided script rather than calling the
Literature Search Europepmc by the numbers
- 1,522 all-time installs (skills.sh)
- +180 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #66 of 911 Databases skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
literature-search-europepmc capabilities & compatibility
- Capabilities
- **`uv`**: read the `uv` skill and follow its set · **user notification**: if license_notification.t · **open access only**: this skill exclusively sea · **never run python3 or python3 c directly**: th · **use the wrapper**: always use the provided scr
- Use cases
- documentation
What literature-search-europepmc says it does
**`uv`**: Read the `uv` skill and follow its Setup instructions to ensure `uv` is installed and on PATH.
## Core Rules - **Open Access Only**: This skill exclusively searches open-access content.
npx skills add https://github.com/google-deepmind/science-skills --skill literature-search-europepmcAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.5k |
|---|---|
| repo stars | ★ 2.6k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 7, 2026 |
| Repository | google-deepmind/science-skills ↗ |
How do I use literature-search-europepmc for the task described in its SKILL.md triggers?
>
Who is it for?
Teams invoking literature-search-europepmc when the user request matches documented triggers and prerequisites.
Skip if: Skip when cached docs are missing, the request is a negative trigger, or another sibling skill owns the workflow.
When should I use this skill?
>
What you get
Step-by-step guidance grounded in literature-search-europepmc documentation and reference files.
- downloaded PDF or XML full text
- paper metadata
- citation bibliography
By the numbers
- Europe PMC indexes over 43 million abstracts
- Europe PMC provides more than 9 million full-text articles
Files
Europe PMC Database
A skill for searching, downloading, and exploring open-access papers from Europe PMC — a comprehensive, free life-science literature database with over 43 million abstracts and 9 million full-text articles.
Prerequisites
1. `uv`: Read the uv skill and follow its Setup instructions to ensure uv is installed and on PATH. 2. User Notification: If LICENSE_NOTIFICATION.txt does not already exist in this skill directory then (1) prominently notify the user to check the terms at https://europepmc.org/ and to always check the license of the papers retrieved by the skill for any restrictions, then (2) create the file recording the notification text and timestamp.
Core Rules
- Open Access Only: This skill exclusively searches open-access content.
The script automatically appends OPEN_ACCESS:y to every search query. Do NOT remove or override this filter.
- NEVER run python3 or python3 -c directly: the system Python does not
necessarily have all key dependencies. Do not attempt to pip install or create new venvs.
- Use the Wrapper: ALWAYS use the provided script rather than calling the
API directly. The script handles rate limiting (1 req/s) and errors.
- Output Files: All subcommands require
--outputto write results to a
file. Read the output file separately to avoid context overflow.
- List Sources. If this skill is used, ensure this is mentioned in the
output AND list the URLs of all papers that were used in producing the output.
Utility Scripts
All commands are subcommands of scripts/europepmc_api.py. Rate limiting and retries are handled automatically.
1. Search (search)
Search Europe PMC by query. Supports DOI lookup, keyword search, author search, PMID lookup, and the full Europe PMC search syntax.
# Look up a paper by DOI
uv run scripts/europepmc_api.py search "DOI:10.1038/s41586-021-03819-2" --output result.json
# Keyword search
uv run scripts/europepmc_api.py search "CRISPR cancer" --max_results 5 --output results.json
# Author search
uv run scripts/europepmc_api.py search "AUTH:Jumper J" --max_results 10 --output results.json
# PMID lookup
uv run scripts/europepmc_api.py search "EXT_ID:34265844 AND SRC:MED" --output result.json
# Sorted by citations
uv run scripts/europepmc_api.py search "machine learning" \
--sort "CITED desc" --max_results 20 --output results.jsonArguments:
-
query(str, required) — search query using Europe PMC syntax -
--output(str, required) — output JSON file path -
--max_results(int, default 10) — maximum results per page (max 1000) -
--result_type(str, defaultcore) —core(full metadata) orlite -
--cursor(str, default*) — cursor mark for pagination; pass the
nextCursorMark value from a previous response to get the next page
-
--sort(str) — sort order, e.g.CITED desc,P_PDATE_D desc
(publication date descending), P_PDATE_D asc
Output: JSON file with three fields:
-
hitCount(int) — total number of matching articles -
nextCursorMark(str) — cursor for next page; empty string if no more pages -
results(list) — array of article metadata objects
Search Syntax Quick Reference:
-
DOI:10.xxxx/yyyy— look up by DOI -
EXT_ID:12345678 AND SRC:MED— look up by PMID -
AUTH:surname initials— author search -
TITLE:keyword— search in title only -
JOURNAL:name— search by journal -
PUB_YEAR:2024or(FIRST_PDATE:[2023-01-01 TO 2023-12-31])— date filter -
HAS_FT:y— restrict to articles with full text in Europe PMC - Boolean operators:
AND,OR,NOT
Note: OPEN_ACCESS:y is automatically appended to all queries. You do notneed to add it manually.
2. Download PDF (download_pdf)
Download an open-access PDF from Europe PMC by PMCID.
uv run scripts/europepmc_api.py download_pdf PMC8371605 --output alphafold.pdfArguments:
-
pmcid(str, required) — PubMed Central ID (e.g.,PMC8371605) -
--output(str, required) — filepath to save the PDF
Output: Saves the PDF to the specified file. Exits with an error if the PMCID is not found or the response is not a valid PDF. Whenever you download a PDF, check the pdf downloaded is not empty or corrupted.
3. Get Full Text (get_fulltext)
Retrieve the full text of an open-access article and save to a file. Returns plain text (XML tags stripped) by default, or raw XML with --format xml.
# Get plain text (default)
uv run scripts/europepmc_api.py get_fulltext PMC8371605 --output fulltext.txt
# Get raw XML
uv run scripts/europepmc_api.py get_fulltext PMC8371605 --format xml --output fulltext.xmlArguments:
-
pmcid(str, required) — PubMed Central ID -
--output(str, required) — output file path -
--format(str, defaulttext) —text(plain text) orxml(raw JATS
XML)
Output: Full text written to the specified file. Exits with an error if the article is not in the Europe PMC open-access subset.
Important: Only articles in the PMC Open Access Subset have full text
available. If retrieval fails, usesearchto check theisOpenAccessfield
and fall back to the abstract.
4. Get Citations (get_citations)
Retrieve articles that cite a given paper.
# Get citations for the AlphaFold paper (PMID 34265844)
uv run scripts/europepmc_api.py get_citations MED 34265844 \
--page_size 25 --output citations.jsonArguments:
-
source(str, required) — source database:MED(PubMed),PMC,PPR
(preprints), PAT (patents)
-
article_id(str, required) — article ID in the source database -
--output(str, required) — output JSON file path -
--page(int, default 1) — page number -
--page_size(int, default 25) — results per page
Output: JSON file with hitCount and citations array.
5. Get References (get_references)
Retrieve the reference list (bibliography) of a given paper.
# Get references from the AlphaFold paper
uv run scripts/europepmc_api.py get_references MED 34265844 \
--page_size 100 --output references.jsonArguments:
-
source(str, required) — source database:MED,PMC,PPR,PAT -
article_id(str, required) — article ID in the source database -
--output(str, required) — output JSON file path -
--page(int, default 1) — page number -
--page_size(int, default 25) — results per page
Output: JSON file with hitCount and references array.
Common Workflows
DOI to PDF
# Step 1: Search for the PMCID
uv run scripts/europepmc_api.py search "DOI:10.1038/s41586-021-03819-2" --output result.json
PMCID=$(jq -r '.results[0].pmcid // empty' result.json)
# Step 2: Download the PDF
uv run scripts/europepmc_api.py download_pdf "$PMCID" --output paper.pdfPMID to Full Text
# Step 1: Find the PMCID from a PMID
uv run scripts/europepmc_api.py search "EXT_ID:34265844 AND SRC:MED" --output result.json
PMCID=$(jq -r '.results[0].pmcid // empty' result.json)
# Step 2: Get the full text
uv run scripts/europepmc_api.py get_fulltext "$PMCID" --output fulltext.txtCitation Graph Traversal
# Find what papers cite a landmark study, then check their references
uv run scripts/europepmc_api.py get_citations MED 34265844 --page_size 50 --output citing.json
# Parse a cited paper's PMID and explore its references
uv run scripts/europepmc_api.py get_references MED <CITING_PMID> --output refs.jsonSearch with Pagination
# First page
uv run scripts/europepmc_api.py search "CRISPR" --max_results 100 --output page1.json
# Extract cursor for next page
CURSOR=$(jq -r '.nextCursorMark // empty' page1.json)
# Next page
uv run scripts/europepmc_api.py search "CRISPR" --max_results 100 --cursor "$CURSOR" --output page2.json# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Europe PMC API CLI — search, download, full text, citations & references.
Usage examples:
uv run europepmc_api.py search "CRISPR" --max_results 5 --output results.json
uv run europepmc_api.py get_fulltext PMC8371605 --output fulltext.txt
"""
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "scienceskillscommon",
# ]
# [tool.uv.sources]
# scienceskillscommon = { path = "../../scienceskillscommon" }
# ///
from __future__ import annotations
import argparse
import json
import os
import re
import sys
import urllib.parse
import xml.etree.ElementTree as ET
from science_skills.skills.scienceskillscommon import http_client
_API_BASE = "https://www.ebi.ac.uk/europepmc/webservices/rest/"
_PDF_BASE = "https://europepmc.org/"
_API_CLIENT = http_client.HttpClient(
_API_BASE, qps=1.0, referer_skill="literature-search-europepmc"
)
_PDF_CLIENT = http_client.HttpClient(
_PDF_BASE, qps=1.0, referer_skill="literature-search-europepmc"
)
def write_output(data, output_file, *, write_as_json=True):
"""Writes data to a file, optionally as JSON.
Args:
data: The data to write. Serialized as JSON when write_as_json is True.
output_file: Path to the output file.
write_as_json: If True (default), serialize data with json.dump. If False,
write data directly as text.
"""
try:
with open(output_file, "w", encoding="utf-8") as f:
if write_as_json:
json.dump(data, f, indent=2)
else:
f.write(data)
print(f"Success! Data written to: {output_file}")
except (OSError, TypeError) as e:
print(f"Error writing to file {output_file}: {e}")
sys.exit(1)
def _extract_all_text(elem):
"""Recursively extract all text content from an XML element."""
parts = []
if elem.text:
parts.append(elem.text)
for child in elem:
parts.append(_extract_all_text(child))
if child.tail:
parts.append(child.tail)
return "".join(parts)
def _xml_to_plain_text(xml_string):
"""Extract article title, abstract, and body text from JATS XML."""
try:
root = ET.fromstring(xml_string)
except ET.ParseError:
# If XML parsing fails, fall back to regex-based tag stripping.
text = re.sub(r"<[^>]+>", " ", xml_string)
return re.sub(r"\s+", " ", text).strip()
sections = []
# Extract article title.
for title in root.iter("article-title"):
t = _extract_all_text(title).strip()
if t:
sections.append(f"# {t}")
break
# Extract abstract(s).
for abstract in root.iter("abstract"):
text = _extract_all_text(abstract).strip()
if text:
sections.append(f"## Abstract\n\n{text}")
# Extract body paragraphs.
for body in root.iter("body"):
body_parts = []
for elem in body.iter():
tag = elem.tag.split("}")[-1] if "}" in elem.tag else elem.tag
if tag == "title":
title_text = _extract_all_text(elem).strip()
if title_text:
body_parts.append(f"\n## {title_text}\n")
elif tag == "p":
para = _extract_all_text(elem).strip()
if para:
body_parts.append(para)
if body_parts:
sections.append("\n\n".join(body_parts))
break # Only the first <body> element.
return "\n\n".join(sections)
# ---------------------------------------------------------------------------
# Subcommands
# ---------------------------------------------------------------------------
def search(query, max_results=10, result_type="core", cursor="*", sort=""):
"""Search Europe PMC and return open-access article metadata."""
# Enforce open-access only: append filter if not already present.
if "OPEN_ACCESS:" not in query.upper():
query = f"({query}) AND OPEN_ACCESS:y"
params = {
"query": query,
"format": "json",
"resultType": result_type,
"pageSize": min(max_results, 1000),
"cursorMark": cursor,
}
if sort:
params["sort"] = sort
url = f"search?{urllib.parse.urlencode(params)}"
print(f"Searching Europe PMC (open access): {query}", file=sys.stderr)
data = _API_CLIENT.fetch_json(url)
results = data.get("resultList", {}).get("result", [])
hit_count = data.get("hitCount", 0)
next_cursor = data.get("nextCursorMark", "")
return {
"hitCount": hit_count,
"nextCursorMark": next_cursor if next_cursor != cursor else "",
"results": results[:max_results],
}
def download_pdf(pmcid, output):
"""Download an open-access PDF from Europe PMC."""
url = f"articles/{pmcid}?pdf=render"
print(f"Downloading PDF for {pmcid}...", file=sys.stderr)
content = _PDF_CLIENT.fetch_bytes(url, timeout=60)
if not content[:5].startswith(b"%PDF"):
sys.exit(
f"Error: Response for {pmcid} is not a valid PDF."
" The article may not be available in open access."
)
out_dir = os.path.dirname(output)
if out_dir:
os.makedirs(out_dir, exist_ok=True)
with open(output, "wb") as f:
f.write(content)
print(f"Saved {len(content)} bytes to {output}", file=sys.stderr)
def get_fulltext(pmcid, fmt="text"):
"""Retrieve the full text of an open-access article by PMCID."""
url = f"{pmcid}/fullTextXML"
print(f"Fetching full text for {pmcid}...", file=sys.stderr)
xml_content = _API_CLIENT.fetch_text(url, timeout=60)
if fmt == "xml":
return xml_content
else:
return _xml_to_plain_text(xml_content)
def get_citations(source, article_id, page=1, page_size=25):
"""Retrieve articles citing a given paper."""
params = urllib.parse.urlencode({
"page": page,
"pageSize": min(page_size, 1000),
"format": "json",
})
url = f"{source}/{article_id}/citations?{params}"
print(f"Fetching citations for {source}/{article_id}...", file=sys.stderr)
data = _API_CLIENT.fetch_json(url)
hit_count = data.get("hitCount", 0)
citations = data.get("citationList", {}).get("citation", [])
return {"hitCount": hit_count, "citations": citations}
def get_references(source, article_id, page=1, page_size=25):
"""Retrieve the reference list of a given paper."""
params = urllib.parse.urlencode({
"page": page,
"pageSize": min(page_size, 1000),
"format": "json",
})
url = f"{source}/{article_id}/references?{params}"
print(f"Fetching references for {source}/{article_id}...", file=sys.stderr)
data = _API_CLIENT.fetch_json(url)
hit_count = data.get("hitCount", 0)
references = data.get("referenceList", {}).get("reference", [])
return {"hitCount": hit_count, "references": references}
# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(
description=(
"Europe PMC API: search, download, full text, citations & references."
)
)
sub = parser.add_subparsers(dest="command", required=True)
# search -------------------------------------------------------------------
p_search = sub.add_parser("search", help="Search Europe PMC")
p_search.add_argument("query", help="Search query")
p_search.add_argument(
"--max_results", type=int, default=10, help="Max results (default 10)"
)
p_search.add_argument(
"--result_type",
default="core",
choices=["core", "lite"],
help="Result detail level (default: core)",
)
p_search.add_argument(
"--cursor",
default="*",
help="Cursor mark for pagination (default: first page)",
)
p_search.add_argument(
"--sort",
default="",
help="Sort order, e.g. 'CITED desc' or 'P_PDATE_D desc'",
)
p_search.add_argument("--output", required=True, help="Output JSON file path")
# download_pdf -------------------------------------------------------------
p_dl = sub.add_parser("download_pdf", help="Download PDF by PMCID")
p_dl.add_argument("pmcid", help="PubMed Central ID (e.g. PMC10462087)")
p_dl.add_argument("--output", required=True, help="Output filepath")
# get_fulltext -------------------------------------------------------------
p_ft = sub.add_parser("get_fulltext", help="Retrieve full text by PMCID")
p_ft.add_argument("pmcid", help="PubMed Central ID (e.g. PMC8371605)")
p_ft.add_argument(
"--format",
dest="fmt",
default="text",
choices=["text", "xml"],
help="Output format: 'text' (stripped plain text) or 'xml' (raw XML)",
)
p_ft.add_argument("--output", required=True, help="Output file path")
# get_citations ------------------------------------------------------------
p_cit = sub.add_parser("get_citations", help="Get articles citing a paper")
p_cit.add_argument("source", help="Source database (e.g. MED, PMC, PPR)")
p_cit.add_argument("article_id", help="Article ID (e.g. PMID or PMCID)")
p_cit.add_argument("--page", type=int, default=1, help="Page number")
p_cit.add_argument(
"--page_size", type=int, default=25, help="Results per page"
)
p_cit.add_argument("--output", required=True, help="Output JSON file path")
# get_references -----------------------------------------------------------
p_ref = sub.add_parser("get_references", help="Get reference list of a paper")
p_ref.add_argument("source", help="Source database (e.g. MED, PMC, PPR)")
p_ref.add_argument("article_id", help="Article ID (e.g. PMID or PMCID)")
p_ref.add_argument("--page", type=int, default=1, help="Page number")
p_ref.add_argument(
"--page_size", type=int, default=25, help="Results per page"
)
p_ref.add_argument("--output", required=True, help="Output JSON file path")
args = parser.parse_args()
if args.command == "search":
result = search(
args.query, args.max_results, args.result_type, args.cursor, args.sort
)
write_output(result, args.output)
print(
f"{len(result['results'])} of {result['hitCount']} result(s).",
file=sys.stderr,
)
if result["nextCursorMark"]:
print(f"Next cursor: {result['nextCursorMark']}", file=sys.stderr)
elif args.command == "download_pdf":
download_pdf(args.pmcid, args.output)
elif args.command == "get_fulltext":
text = get_fulltext(args.pmcid, args.fmt)
write_output(text, args.output, write_as_json=False)
elif args.command == "get_citations":
result = get_citations(
args.source, args.article_id, args.page, args.page_size
)
write_output(result, args.output)
print(
f"{len(result['citations'])} of {result['hitCount']} citation(s).",
file=sys.stderr,
)
elif args.command == "get_references":
result = get_references(
args.source, args.article_id, args.page, args.page_size
)
write_output(result, args.output)
print(
f"{len(result['references'])} of {result['hitCount']} reference(s).",
file=sys.stderr,
)
if __name__ == "__main__":
main()
Related skills
How it compares
Use literature-search-europepmc for open-access life-science papers on Europe PMC; use general web search skills for non-academic documentation.
FAQ
What does literature-search-europepmc do?
>
When should I use literature-search-europepmc?
>
What are common prerequisites?
--- name: literature-search-europepmc description: > Search Europe PMC for scientific literature and download open-access full texts and PDFs.
Is Literature Search Europepmc safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.