Check whether the references in a bibliography actually exist, against OpenAlex, Crossref and arXiv.
Project description
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:
- A transport failure is never a verdict. If every index errors you get
error, neverunverified. Reporting a real paper as missing because DNS failed is the worst thing this tool could do. - 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.
- 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 innotes. 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.
unverifiedon 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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file citeverify-0.1.0.tar.gz.
File metadata
- Download URL: citeverify-0.1.0.tar.gz
- Upload date:
- Size: 25.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e227123a5180e42513ad10f12b4bebf6fffc992ee290aecc89e808e1ad3528ea
|
|
| MD5 |
48f7ca61cc9bab02c1b8d2bff18d5f6d
|
|
| BLAKE2b-256 |
87eeb673d1821cc87c3886f93d7c516bc356d66ea8abd50ff60455bb3fbc1226
|
File details
Details for the file citeverify-0.1.0-py3-none-any.whl.
File metadata
- Download URL: citeverify-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
567e1226f08e459141dd9294ee549bf10df58676625121f39890eb0af2cda22c
|
|
| MD5 |
2241b8c1c2b8f57ea8f4c4eb70a8a17a
|
|
| BLAKE2b-256 |
e58f2e7be79ac5fee31b974a4d102d650cea28be1a58398efb964247f2c19289
|