Literature Search Europepmc
Google DeepMindOfficialPublished by Google DeepMind
- Skill
- Tool integration
- Science and research
- Apache-2.0
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides an interface to the Europe PMC database, allowing users to search for life-science literature, download open-access PDFs, and extract full-text content. It supports querying by DOI, PMID, author, or keyword, and includes functionality to retrieve citation lists and bibliographies for academic research workflows.
What it does
- Searches the Europe PMC database for open-access scientific articles.
- Downloads open-access PDF documents using a PMCID.
- Retrieves full-text content in plain text or XML format.
- Fetches lists of articles that cite a specific paper.
- Extracts reference lists and bibliographies for academic papers.
- Supports paginated search results for large datasets.
When to use it
- When you need to find open-access research papers based on specific keywords or identifiers.
- When you need to download the full text or PDF of a life-science article.
- When you are performing a literature review and need to map citation networks.
- When you need to extract bibliographies from specific academic publications.
Advantages
- Automates the retrieval of scientific literature and metadata.
- Handles rate limiting and pagination for large-scale searches.
- Provides direct access to open-access full-text content.
- Integrates with standard research identifiers like DOI and PMID.
Usage examples
- “Find the latest open-access papers on CRISPR cancer research.”
- The agent performs a keyword search on Europe PMC and returns a list of matching articles with their metadata.
- “Download the PDF for the paper with PMCID PMC8371605.”
- The agent uses the download_pdf command to save the specified article as a PDF file.
- “Get all papers that cite the study with PMID 34265844.”
- The agent retrieves a list of citing articles from the Europe PMC database and saves the results to a JSON file.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/google-deepmind/literature-search-europepmc/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip literature-search-europepmc.zip -d .agents/skills
Documentation by Google DeepMind
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
uv: Read theuvskill and follow its Setup instructions to ensureuvis installed and on PATH.User Notification: If .licenses/literature_search_europepmc_LICENSE.txt does not already exist in the workspace root 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:yto 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 thenextCursorMarkvalue 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 articlesnextCursorMark(str) — cursor for next page; empty string if no more pagesresults(list) — array of article metadata objects
Search Syntax Quick Reference:
DOI:10.xxxx/yyyy— look up by DOIEXT_ID:12345678 AND SRC:MED— look up by PMIDAUTH:surname initials— author searchTITLE:keyword— search in title onlyJOURNAL:name— search by journalPUB_YEAR:2024or(FIRST_PDATE:[2023-01-01 TO 2023-12-31])— date filterHAS_FT:y— restrict to articles with full text in Europe PMCBoolean operators:
AND,OR,NOT
Note:
OPEN_ACCESS:yis automatically appended to all queries. You do not need 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, use
searchto 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,PATarticle_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.jsonFiles
- ATTRIBUTION.mdAdded by AutoGineer936 B
- LICENSELicence file added by AutoGineer11.1 KiB
- SKILL.md8.2 KiB
- references/citation.bib2.0 KiB
- scripts/europepmc_api.pyScript11.2 KiB
scripts/europepmc_api.py
# 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 = [
# "polite-http",
# ]
# ///
from __future__ import annotations
import argparse
import json
import os
import re
import sys
import urllib.parse
import xml.etree.ElementTree as ET
from polite_http 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="https://github.com/google-deepmind/science-skills/tree/main/literature-search-europepmc",
)
_PDF_CLIENT = http_client.HttpClient(
_PDF_BASE,
qps=1.0,
referer="https://github.com/google-deepmind/science-skills/tree/main/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()
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Python
Security
What our scanner noticed· Low risk
- Instructions that use the network
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Google DeepMind
- Publisher
- Google DeepMind (Official)
- Source
- google-deepmind/science-skills · skills/literature_search_europepmc · commit 4de6786
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Google DeepMind or Google DeepMind.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download literature-search-europepmc.zip