Case study

RefChecker

Category
Backend / Cloud
Period
Jun 2026 – Sep 2026

Reference metadata verification with streaming results across three academic APIs

  • Next.js
  • React
  • TypeScript
  • Crossref API
  • OpenAlex API
  • Semantic Scholar API
  • Vitest
  • Vercel
  • NDJSON Streaming

Context

Checking a bibliography is manual and slow. You paste a reference into a search engine, read the publisher page, and judge whether the paper you cited is the paper you meant. For a thesis chapter with sixty references, that is hours of repetitive judgement, and the failure mode is quiet: a citation that does not exist in the public record looks exactly like one that does until someone checks.

Student reviewers face this on every bibliography they touch. Lab groups face it during literature review. Both need the same thing — a fast signal on which references deserve a human's attention, and the evidence behind that signal.

Problem

Bibliographic metadata is scattered across providers that disagree with each other and are all rate limited:

  • Crossref is authoritative for anything with a registered DOI, but it knows nothing about works that never registered one.
  • OpenAlex has broader coverage, with looser metadata.
  • Semantic Scholar covers another slice again, and rate limits anonymous traffic hard.

Querying one provider means missing whatever only the others know. Querying them naively in sequence means the slowest provider sets the latency for the entire batch, and a 429 from the first one loses the reference entirely.

And the answer is not binary. A reference either matches or it does not, but the interesting cases are in between: the title matches but the year does not, the author list is a subset, or two sources disagree about the journal.

Solution

One endpoint that accepts a pasted bibliography, splits it into individual references, and verifies each one against a chained provider fallback — Crossref first, then OpenAlex, then Semantic Scholar — stopping at the first provider that returns usable metadata.

Parsing is first-party and format-aware. A style detector identifies APA, Harvard Elsevier, IEEE and MLA, then a parser normalises the DOI, title, authors and year out of each. Wrapped lines are rejoined rather than treated as new references, so a bibliography pasted straight out of Word or a PDF survives intact.

Results are returned as newline-delimited JSON. Each line is one event, so a reference appears on screen the instant it is verified instead of waiting for the slowest one in the batch. Results also export to CSV, with conflicting fields and the matching evidence included as columns.

Architecture

Next.js App Router with a single route handler at POST /api/verify. The whole runtime dependency list is next, react and react-dom — the citation parser, style detector, matcher, scorer and CSV writer are all first-party.

Providers sit behind one academic-provider interface, so the chain is a list rather than branching logic. Crossref is called sequentially with retry on 429, and the verification service falls through to the next provider when one returns nothing.

The matcher is field-level rather than a single similarity score. Title and author similarity, year agreement and DOI equality are each evaluated separately, journal names are compared after stopword removal, and abbreviations are handled explicitly — J. Nat Commun has to match Journal of Natural Communications, which naive prefix matching gets wrong. A dedicated module flags fields where sources contradict each other, which is usually the actual reason a reference deserves review.

Input is bounded in verification-limits.ts: twenty unique references and 50,000 characters per request, with four concurrent verifications. Over-limit input returns 413 with a plain JSON body rather than an open stream, so the failure is legible instead of truncated mid-batch.

Tests use mocked provider responses, so the suite never depends on the network or on external credentials.

Challenges

Results arrive out of order. References verify concurrently, so result events can land in any sequence. Each carries an index and the UI renders by index, which is what keeps the display stable while the stream stays fast.

Streaming changes the error contract. Validation errors have to be plain 400/413 JSON responses, but a failure mid-stream cannot be — the status line is already sent. Those are emitted as an error event instead.

"Verified" is an overclaim. The tool checks whether public metadata agrees with the citation, not whether the paper is genuine, sound, or peer reviewed. Saying so plainly in the README and in the interface was more useful than a cleaner-sounding claim that would mislead.

CSV has to survive Excel. Excel applies the locale's delimiter, so a comma separated file can open as one giant column. Export follows RFC 4180, and the joined author list uses and rather than a semicolon specifically so the column stays intact whichever delimiter Excel decides on.

Results

Deployed and running at refchecker.vercel.app.

Four citation styles supported, three academic providers chained with fallback, and a confidence score with its supporting evidence attached to every result — the evidence matters as much as the verdict, since a reviewer needs to know why a reference was flagged.

Test coverage spans the parser, style detection, each provider adapter, the verification service, scoring, CSV export and the API endpoint, all against mocked responses.

Screenshots

03