Skip to main content

citeverify

Check whether the references in a bibliography actually exist.

Fabricated citations are now a routine failure mode. LLM-drafted reference lists invent plausible-looking papers; copy-paste drift corrupts real ones. citeverify cross-checks every reference against OpenAlex, Crossref and arXiv, and tells you what it could and could not confirm.

pip install citeverify
citeverify check references.txt
Checked 6 references
----------------------------------------------------
  verified              4   ( 66.7%)
  year mismatch         1   ( 16.7%)
  NOT FOUND             1   ( 16.7%)

2 reference(s) need attention:

  [NOT FOUND] Nonexistent, A. (2023). Quantum entanglement in transformer attention heads.
      closest: Transformer quantum state: A multipurpose model for quantum many-body problems (2023)
  [year mismatch] Smith, J. (1897). Attention is all you need. NeurIPS.
      closest: Attention Is All You Need (2017)

Why the verdicts are split the way they are

Most of the design effort went into not crying wolf. A tool whose output reads as an integrity signal has to be careful about what it asserts.

Verdict Meaning
verified A record matching title, authors and year was found.
year_mismatch The work is real; the year disagrees. This is a typo.
author_mismatch The title matches a real work but the authors don't. Often a merged or mis-copied reference.
partial Weak similarity only. Needs a human.
unverified Nothing plausible found in any index.
error A lookup failed. Not a finding about the reference.

Three rules follow from this, and they are enforced by tests:

  1. A transport failure is never a verdict. If every index errors you get error, never unverified. Reporting a real paper as missing because DNS failed is the worst thing this tool could do.
  2. Missing author data never counts against a reference. Standards, datasets and technical reports routinely list no authors. Absence of evidence is not evidence of fabrication.
  3. Wrong metadata is distinguished from invention. A real paper cited with a bad year resolves to the right work and is reported as a year error. A typo and a hallucination need different fixes.

Usage

CLI

citeverify check refs.txt                      # human-readable report
citeverify check refs.txt --json out.json      # machine-readable results
citeverify check refs.txt --cache .cv.json     # reruns become free
citeverify check refs.txt --strict             # exit 1 if anything suspicious (CI)
citeverify parse refs.txt                      # parse only, no network
citeverify check - < refs.txt                  # read stdin

Give --mailto you@example.org to enter the OpenAlex/Crossref polite pool, which gets you faster and more reliable service. Thresholds are tunable: --verified-threshold, --partial-threshold, --year-tolerance.

Exit codes: 0 ran cleanly · 1 suspicious references found under --strict (error results never trigger this) · 2 usage or input error.

Python

from citeverify import parse_references, verify_all, summarize

refs = parse_references(open("references.txt").read())
results = verify_all(refs, cache=Cache(".cv.json"))

print(summarize(results))                       # {'verified': 4, 'unverified': 1, ...}
for r in results:
    if r.suspicious:
        print(r.status, "--", r.citation["raw"])

Already have structured data? Skip the parser. verify() and verify_all() accept plain dicts with title / authors / year / doi / arxiv_id.

Air-gapped and restricted networks

All network access goes through a single injectable callable. Nothing else in the package performs I/O:

from citeverify import verify, OpenAlexSource

def my_fetcher(url, headers):
    return my_corporate_proxy.get(url, headers=headers).text

verify(citation, sources=[OpenAlexSource(fetcher=my_fetcher)])

Point it at an on-prem OpenAlex mirror, a proxy, or a cached snapshot. The entire test suite runs through this seam, so it is a supported path and not an afterthought.

Install

pip install citeverify

Zero hard dependencies. Standard library only. This is deliberate: the people who most need citation checking (institutional review offices, air-gapped labs) are the least able to approve a dependency tree.

Requires Python 3.9+.

Limits, and please read them

  • The parser is heuristic. Reference lists are a hundred house styles wearing a trenchcoat. Entries parsed by segmentation are marked parse_confidence: "low" and flagged in notes. Feed structured data when you have it.
  • Matching is lexical. Title similarity is token overlap with a substring escape hatch. It does not understand meaning.
  • Coverage gaps are real. Books, theses, standards, and much non-English and pre-1970 work are thinly indexed. unverified on those usually means the work is not indexed. It does not mean the work is fake.
  • It does not judge whether a citation is appropriate, or whether the cited work supports the claim attached to it. It answers exactly one question: does a record matching this reference exist?

Treat the output as a triage queue for a human, never as a verdict.

Provenance

The matching logic was developed and hardened while verifying a 1,473-reference corpus across ~101 papers. Every one was driven to a resolved status. This is extracted from tooling that had to work at that scale.

Development

pip install -e ".[dev]"
pytest            # 76 tests, all offline. No network required.

Licence

MIT.

Metadata

Release files for citeverify 0.1.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 citeverify 0.1.0
File Size Uploaded Provenance Actions
citeverify-0.1.0.tar.gz 25.6 kB Without provenance Details for citeverify-0.1.0.tar.gz

Built distribution (wheel)

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

Total release size: 46.1 kB

Release files / citeverify-0.1.0.tar.gz

Download URL citeverify-0.1.0.tar.gz
Size 25.6 kB
Tags Source
SHA-256 checksum
How to use checksums
e227123a5180e42513ad10f12b4bebf6fffc992ee290aecc89e808e1ad3528ea
BLAKE2b-256 checksum
How to use checksums
87eeb673d1821cc87c3886f93d7c516bc356d66ea8abd50ff60455bb3fbc1226
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release files / citeverify-0.1.0-py3-none-any.whl

Download URL citeverify-0.1.0-py3-none-any.whl
Size 20.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
567e1226f08e459141dd9294ee549bf10df58676625121f39890eb0af2cda22c
BLAKE2b-256 checksum
How to use checksums
e58f2e7be79ac5fee31b974a4d102d650cea28be1a58398efb964247f2c19289
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Security for citeverify 0.1.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

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