Jaswant Singh

← Selected work / 01

AuctionWire

Reading the certificate on every graded card, from whatever photo the auction house happened to post.

Role
Designed and built the OCR service and its model
For
AuctionWire, at Intelligaia Technologies
Stack
Python, FastAPI, Qwen vision models (QLoRA), PostgreSQL, Gemrate API
Platform
Next.js and Node.js

85% → 98%

cert-number accuracy after fine-tuning a Qwen vision model with QLoRA

95%+

accuracy on rotated cards

71

listing sources registered: auction houses, marketplaces and eBay

0

third-party OCR providers. The service replaced the paid one

01

The problem

A graded card is only worth what its slab says. To trust a listing, AuctionWire needs the grader, the certificate number and the grade, and they have to belong to this card.

AuctionWire gathers graded trading-card listings from dozens of auction houses and eBay into one place. The photos are whatever each house posted: small, rotated, glare on the plastic, sometimes the back of the slab first. Every grader prints its label differently, and the certificate formats differ too. PSA uses 8 to 10 digits, BGS 10 to 11 with leading zeros, and SGC 7 to 10 with an optional dash.

The failure that matters isn't a card we can't read. It's a card we read confidently and wrongly: a plausible cert that belongs to some other card silently attaches the wrong population, sales history and value. So the system had to be built around verification, not just recognition.

02

How a card is read

Six steps, in order, stopping at the first one that settles the card.

  1. 1

    Fetch the image

    Download the listing photo, retrying through a proxy or a headless browser for hosts that block plain clients. Downscale to the model's image budget. If the first image shows no slab, try the next.

  2. 2

    Read the label

    A fine-tuned Qwen vision model returns grader, cert, grade, year, brand, player and card number as JSON, with a confidence. It is told to read the grader's name, not guess from label colour.

  3. 3

    Guard the cert

    Reject certs that can't belong to this card before spending a registry call: placeholder values, wrong formats, and “attractor” certs the model tends to write onto unrelated cards.

  4. 4

    Verify with Gemrate

    Look the cert up in the Gemrate registry and compare its grade with the label's. For PSA and SGC a contradiction fails the read; dual-graded slabs count either grade.

  5. 5

    Escalate

    If the read fails, is unsure, or the cert isn't found, a larger 30B vision model re-reads it. Then comes a registry search by the card's title. What's still unresolved goes to a person.

  6. 6

    Store one record

    Write one verified record per listing: the label's reading plus Gemrate's full record of the cert, including set, parallel, subgrades and population.

03

Guarding against confident mistakes

Vision models don't just fail to read. Some numbers they love to write, and those numbers turn up on cards they don't belong to.

  • Attractor certs. Any cert seen on 25 or more distinct cards is treated as suspect. The list is relearned from the corpus every 12 hours and cached, so startup never waits for it.
  • Placeholders and formats. Values like 1000000000 or 8888888888 are rejected outright, and each grader's cert shape is enforced.
  • Grade agreement. A cert that exists but disagrees with the label's grade is usually another card. Enforcing this for PSA showed that 64.7% of the PSA certs that still disagreed were exactly that.
  • One outcome per read. Every read ends in exactly one named state: verified, verified by title, not found, grade mismatch, consensus, low confidence, no slab, or queued for review. Nothing is silently “probably fine”.
04

Evaluated like code

Any change to the prompt, model, adapter or image budget runs against a frozen eval set and is compared with the production baseline. The gate fails the change if cert accuracy on real slabs drops more than a point overall, or more than three for any grader with 30 or more cards. It also fails if more real slabs get answered “no slab”.

The set is 615 production images, with Gemrate's record as ground truth for every slab. It over-weights the hard cases on purpose: 165 of them are cards that only the fallback model, a retry or a person got right.

The larger 30B model is faster, but it fails the gate. Its BGS cert accuracy is 7.7 points lower and it misses 2.6% of slabs, so it stays the fallback and the fine-tuned model reads first.

Baseline on eval set v1 (615 images)
MetricFine-tuned (prod)30B fallback
Cert exact, real slabs95.4%95.0%
Grade93.3%92.9%
Real slabs missed0.0%2.6%
No-slab images correct100%100%
Median latency4.5 s3.0 s
The set: 400 verified · 165 hard · 50 with no slab
05

Live first, backlog ahead

Ahead of time

A background worker reads the backlog across every source, so most cards are already verified before anyone opens them.

Live comes first

When a user opens a card, the live request pauses the backlog's next model call. Concurrent requests for the same listing merge into one, and stored results answer instantly. Load tested at p95 5.8 s with four calls running at once.

People close the loop

Cards neither model can settle land in a review console: approve, correct, mark “no slab” or reject. Every review is stored as a training sample for the next fine-tune.

06

What's still hard