Skip to main content

Deterministic bibliography verifier for .bib files and PDFs

Project description

citation-checker

A deterministic bibliography verifier for .bib files and PDFs. Checks whether cited works exist in authoritative academic databases and whether core metadata (title, authors, year, venue) actually matches the given citation.

How It Works

Each bibliography entry is verified through a priority-ordered strategy chain:

  1. DOI → CrossRef — direct lookup by DOI; the most reliable path
  2. arXiv eprint → arXiv Atom XML API — for preprints with an eprint field
  3. Title + author → CrossRef bibliographic search — for entries without a DOI
  4. Title → OpenAlex — fallback when CrossRef search finds nothing
  5. Title + author → Semantic Scholar — additional fallback with strong ML/CS venue coverage (PMLR, NeurIPS, ICML, etc.)
  6. URL → web title extraction — for news and media sources (Bloomberg, NYT, Reuters, etc.)

All entries are checked concurrently with per-host rate limiting to stay within API guidelines.

Installation

Requires Python ≥ 3.11.

pip install citation-checker

Dependencies: bibtexparser, pymupdf, httpx, rapidfuzz, rich

Quick Start

# Check a .bib file
citation-checker refs.bib --mailto you@email.com

# Check a PDF's bibliography
citation-checker paper.pdf --mailto you@email.com

# Save a JSON report and show fuzzy match scores
citation-checker refs.bib --mailto you@email.com --output report.json --show-scores

# Show what the database actually found (DB title + authors columns)
citation-checker refs.bib --show-remote

# Only show problems
citation-checker refs.bib --filter-status MISMATCH NOT_FOUND ERROR

# Check specific cite keys
citation-checker refs.bib --filter-keys Vaswani17 LeCun89

Tip: Pass --mailto your@email.com to join the CrossRef polite pool and get higher rate limits.

CLI Reference

Flag Default Description
bib_file required Path to a .bib or .pdf file
--output, -o PATH Write a JSON report to this file
--mailto EMAIL Your email for the CrossRef polite pool
--openalex-key KEY OpenAlex API key for higher rate limits
--timeout SECS 10.0 Per-request HTTP timeout
--retries N 3 Max retries per request
--concurrency N 10 Max simultaneous entry checks
--no-check-urls off Disable supplementary URL reachability checks
--allow-local-urls off Allow URL checks to hit private / loopback / link-local IPs (off by default to avoid SSRF on hostile .bib files)
--show-scores off Show title/author fuzzy scores in the table
--show-remote off Show the title and authors returned by the matched database
--filter-keys KEY... Only check these cite keys
--filter-status S... Only display entries with these statuses
--quiet off Print summary only; suppress table
--json-only off No terminal output; write JSON only (requires --output)
--verbose off Enable debug logging to stderr

Exit codes: 0 = all OK · 1 = MISMATCH or ERROR found · 2 = parse/file error · 3 = config error

Verification Statuses

Status Meaning
VERIFIED Found in an external database; title, authors, and year match
MISMATCH Found, but one or more core fields differ significantly
NOT_FOUND Not found in any database
GREY_LITERATURE Software, dataset, government report, or news article — not expected in academic DBs
UNVERIFIABLE Too little metadata (no title or authors) to search with
ERROR Network or API failure for this entry

Fuzzy Matching

Field comparison uses RapidFuzz:

  • Title: fuzz.ratio ≥ 85 after NFKD normalization and LaTeX artifact stripping
  • Authors: per-author best-match token_sort_ratio ≥ 80 — handles "Last, First" vs "First Last" ordering; abbreviated first names (e.g., "R. Smith" vs "Robert Smith") are tolerated as soft warnings
  • Year: exact integer match; a mismatch always forces MISMATCH regardless of other scores

Author scores between 55 and 80 produce a warning but do not by themselves trigger MISMATCH.

PDF Input

When given a .pdf file, citation-checker extracts the bibliography section using PyMuPDF and auto-detects the reference list format -- for instance:

  • Numbered ([1] Author, A. Title. Venue, year.) — brackets or parenthesised numbers
  • Author–year (Surname, A. (year). Title. Venue.) — common in economics and some CS venues

Cite keys depend on the detected style:

  • Numbered styles (ACM [1], plain 1.) keep the printed reference number as the cite key (1, 2, 3, …), so it lines up with how the work is cited in the body ([12]).
  • All other styles derive the key from the first author name-token as printed, plus the year. That token is the surname for Last, First or Initial. Last lists (Wallace1996, Fisher2009), but the given name for First Last lists (Daron2018) — the parser keys off whichever name appears first in the PDF, since it cannot reliably know which is the surname.

When two entries share the same base key, a letter suffix is appended to the second and later occurrences (Vaswani2017, Vaswani2017a; 5, 5a).

Extracted entries go through the same verification pipeline as .bib entries. No DOI or arXiv eprint is assumed unless one is found in the text.

Grey Literature

Entries on code/data hosting sites (GitHub, Zenodo, Hugging Face), government and national lab sites (nlr.gov, epa.gov, eia.gov, etc.), or corporate technical resources are classified as GREY_LITERATURE and skipped in academic database searches — they are not expected to appear in CrossRef or Semantic Scholar.

Entries whose URL points to a supported news or media domain (Bloomberg, Financial Times, NYT, Reuters, WSJ, The Guardian, BBC, Wired, MIT Technology Review, and more) are verified by fetching the page and comparing the article title. A match counts as VERIFIED.

JSON Report

{
  "meta": {
    "tool_version": "1.0.3",
    "generated_at": "...",
    "bib_file": "refs.bib",
    "total_entries": 218,
    "elapsed_seconds": 61.4,
    "thresholds": { "title_score": 85.0, "author_score": 80.0 },
    "counts": { "VERIFIED": 190, "MISMATCH": 5, "NOT_FOUND": 8, ... }
  },
  "results": [
    {
      "cite_key": "Vaswani17",
      "entry_type": "article",
      "status": "VERIFIED",
      "strategy": "doi_crossref",
      "local":  { "title": "...", "authors": [...], "year": 2017, "doi": "...", "url": null, "eprint": null },
      "remote": { "title": "...", "authors": [...], "year": 2017, "source": "crossref" },
      "scores": { "title_score": 100.0, "author_score": 95.3, "year_match": true },
      "url_reachable": null,
      "error_message": null,
      "warnings": []
    }
  ]
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

citation_checker-1.0.3.tar.gz (112.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

citation_checker-1.0.3-py3-none-any.whl (79.6 kB view details)

Uploaded Python 3

File details

Details for the file citation_checker-1.0.3.tar.gz.

File metadata

  • Download URL: citation_checker-1.0.3.tar.gz
  • Upload date:
  • Size: 112.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.2

File hashes

Hashes for citation_checker-1.0.3.tar.gz
Algorithm Hash digest
SHA256 ccc122ae5acd0afb250df39dc3726d43a4f2b0e9fee5528f962fd1813bb3cabd
MD5 77d01464146b6de2a4e8a48de3ec8c2e
BLAKE2b-256 65552e3a570989ec5258db24d7e9bd45fd33af3690797a5d5cf01c1cb576f840

See more details on using hashes here.

File details

Details for the file citation_checker-1.0.3-py3-none-any.whl.

File metadata

File hashes

Hashes for citation_checker-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 3f33a8b42255169c44fdd71f317f430fc1fc86ab4736adb9f79a92d9ae91e237
MD5 6167ac01d9a4bfeedbce4623001effd0
BLAKE2b-256 a71583e58215325547d6ac5ad13725fd1f626302bb4a522342de69daa1a4f0f9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page