retraction scans your manuscripts, bibliographies, and reference lists for
citations to retracted papers, so you do not cite discredited work. It reads a
wide range of document and bibliography formats, extracts and normalizes
identifiers, checks them against retraction data, and returns a tidy,
confidence-scored report.
The default data source is the Retraction Watch database, served through the XeraRetractionTracker API. Crossref and OpenAlex are available as additional, reconcilable sources.
# Development version from GitHub:
# install.packages("pak")
pak::pak("choxos/retraction")library(retraction)
# Check a manuscript, bibliography, or reference file.
result <- check_file("manuscript.Rmd")
result
# Check a vector of DOIs or PMIDs directly.
check_dois(c("10.1016/S0140-6736(97)11096-0", "10.1038/s41586-020-2649-2"))
# Check a data frame of references (columns are auto-detected).
check_refs(retraction_example)A result is a tidy tibble, one row per reference, with the retraction status,
an is_retracted flag, a match confidence, the retraction_date,
days_since_retraction, the reason, and which sources confirmed it. Printing
it gives a compact summary and lists the flagged citations.
Two levels of parsing:
- Structured (identifiers read from fields): BibTeX and BibLaTeX (
.bib), CSL-JSON (.json), RIS (.ris), EndNote XML, and JATS XML. - DOI scraping (DOIs found in the text): Word (
.docx), PDF (.pdf, needs the optionalpdftoolspackage), and text documents (.Rmd,.qmd,.tex,.md,.txt,.html). In these formats, references that do not carry a DOI in the text are not detected.
Given a PMID, PMCID, DOI, title, or a whole reference, check_pmc() resolves it
to a PubMed Central article, tells you whether the open-access full text is
available, and if so checks that article's reference list for retractions.
res <- check_pmc(c("PMC5334499", "10.1371/journal.pone.0000217", "29939664"))
pmc_articles(res) # open-access status, reference count, and retracted count per input
retracted(res) # the retracted references foundEvery check_*() function takes a sources argument. The default is "xera"
(Retraction Watch). Also available: "crossref", "openalex", "europepmc",
"ncbi" (PubMed), "datacite", and "preprint" (arXiv/bioRxiv withdrawals).
You can query more than one, or "all", and have the results reconciled:
check_dois("10.1016/S0140-6736(97)11096-0",
sources = c("xera", "europepmc", "ncbi"))
list_backends() # available sources
check_dois("10.1126/science.aac4716", sources = "all")
options(retraction.sources = c("xera", "openalex")) # set a session defaultWhen several sources are queried, the highest-priority match sets the verdict,
every confirming source is recorded (with dissenters in disagreeing), and a
disagreement flag is raised when sources do not agree.
Two caveats worth knowing:
- OpenAlex's
is_retractedflag is itself derived from Retraction Watch, so Xera and OpenAlex are not fully independent; a disagreement between them usually reflects OpenAlex lagging rather than a genuine conflict. - Crossref and OpenAlex detect retractions only. Corrections, expressions of
concern, and reinstatements come from the Retraction Watch (
xera) source.
Offline mode is fully local: it does not contact the network. (An optional
staleness notice is available with options(retraction.check_freshness = TRUE),
which makes one small request during offline checks.)
For bulk checking, privacy (checking an unpublished manuscript sends only DOIs), or working without a connection, build a local snapshot once and match against it. Updates are incremental, adding new retractions rather than re-downloading everything.
retraction_sync() # download once
check_file("manuscript.Rmd", offline = TRUE)
retraction_sync() # later: incremental updateIf your snapshot falls behind the live database, the package tells you when you run an offline check.
result <- check_file("manuscript.docx")
render_report(result, "report.html") # self-contained HTML, no extra software
render_report(result, "report.md", format = "md")Within a source, matching runs a cascade: exact DOI, then PMID (matched directly against the Retraction Watch corpus, both online and offline, falling back to OpenAlex only to obtain a DOI for the other sources), then fuzzy title matching for references that carry no identifier. When several sources are selected they are all queried and their verdicts reconciled. Exact identifier matches are asserted with high confidence; fuzzy matches are reported as "possible" so you can verify them, and are never asserted as retracted. Fuzzy and PMID matching are most reliable in offline mode, where the full corpus is available locally.
The confidence column is a match-quality heuristic (exact identifier vs. fuzzy
title, adjusted for year and author agreement), not a calibrated probability.
A citation of a retraction notice (rather than of the retracted work) is recognized and not flagged. A work that was retracted and later reinstated is reported as reinstated, not flagged.
Retraction data comes from the
Retraction Watch database, made openly available
through Crossref and served
by XeraRetractionTracker. The optional
Crossref and OpenAlex backends use their public APIs; OpenAlex's is_retracted
flag is itself derived from Retraction Watch. See the LICENSE.note file shipped
with the package (and in the source repository) for the full data-provenance
note covering every source.
Parts of retraction were developed with the assistance of AI coding tools
(Anthropic's Claude, via Claude Code), including code drafting, documentation,
and test scaffolding. All AI-assisted output was reviewed, tested, and verified
by the author, who is solely responsible for the package and its correctness.
GPL-3. See the LICENSE file.
