Malaika AfridiWorkAboutAvailable for work
← All work

Data pipeline · 2026

Dental Price Matcher

Turns a weekly dental supply order into a ranked list of cheaper places to buy it. Upload the distributor's order PDF; the pipeline parses every line, sweeps the open web for the same product, verifies each candidate page, and returns three Excel workbooks showing exactly where the practice is overpaying.

Dental Price Matcher admin portal
The admin portal — one door in, and a parse preview before anything is spent.

Overview

A dental practice orders the same twenty-odd consumables every week from a single distributor. Nobody has time to price-check twenty SKUs across a dozen suppliers by hand, so the practice quietly overpays — every week, indefinitely.

Parsing runs as a token state machine over PyMuPDF output with an OCR fallback for scanned orders, and every row must satisfy `qty × unit ≈ extended` before it is accepted — so misalignment fails loudly instead of producing phantom line items.

Role

Backend & frontend

Timeline

2026 · shipped

Stack

FastAPI · PythonReact · TypeScript · ViteSQLiteGroq llama-3.3-70bSerpAPI · Firecrawl
1Intake

One door in, and a preview before anything is spent

The practice drops in the week’s distributor PDF. Before a single API call is made, the parser’s reading is shown back for confirmation — every SKU, quantity, unit and extended price. A bad parse is caught here rather than after it has burned credits and produced a report nobody can trust.

Parsed line items shown for review before the price search
Parsed preview — every row must satisfy qty × unit ≈ extended before it is accepted, so column misalignment fails loudly instead of inventing line items.
2The pipeline

Watch it think

Enrichment, sweep and verification run as visible stages rather than a spinner. Groq or Gemini reads each free-text description into brand, variant, size and pack quantity; SerpAPI sweeps the open web with no hardcoded supplier list; Firecrawl re-reads every candidate page with JavaScript rendering. The activity log names what each service is doing and when.

Live processing at five seconds, AI enrichment active
Five seconds in — the PDF is parsed and enrichment has started.
Live processing at twenty seconds
Twenty seconds — unattended runs fail loudly, so every stage is named and timestamped.
3The output

Three workbooks and a paper trail

Every completed run is kept: exact price matches sorted by total savings, alternate purchases driven by a client-editable equivalency table, and a full evidence file carrying every URL, price, condition and rejection reason. A price movement can always be traced back to the page it came from.

Order history with the three downloadable reports per run
History — each run keeps its three workbooks and can be re-run against the same order.
4Configuration

Owned by the practice, not by me

Providers and keys live in the admin portal, masked and testable in place. The LLM provider is switchable, excluded domains and the equivalency table are editable without touching code — so the rules that decide what counts as a match stay in the client’s hands.

Admin settings — API providers and keys
Settings — keys stay masked, each connection testable before a run depends on it.

The challenge

What made it hard

A PDF is not data. Column misalignment in a parsed order invents line items that were never on it, and a phantom row poisons every number downstream.

Free-text product descriptions do not match across suppliers. "Same product" has to survive different brand spellings, pack sizes, variants and units before a price comparison means anything.

Search results lie. A shopping listing's title often disagrees with the page it points at, plenty of supplier prices sit behind a login wall, and category pages masquerade as products.

Approach

How it was built

Each line is enriched by Groq's llama-3.3-70b into brand, variant, size/form, pack quantity and MPN, then swept against SerpAPI Google Shopping with an organic backfill. There is no hardcoded supplier list, so any legitimate seller can surface; request pacing and backoff keep the whole thing inside a free-tier rate limit.

Every candidate page is re-read by Firecrawl with JS rendering against a JSON schema — price, pack, variant and login-wall status — and page content always beats the search-result title. Matching then combines deterministic pre-checks (price sanity, volume normalization, exact pack equality) with AI validation against four criteria: brand, product name, size/form and pack quantity.

The filtering rules are deliberately harsh and client-editable without code changes: excluded domains and their subdomains dropped, category and search URLs barred from ever becoming a match, pack-mismatched candidates kept out of the primary report at any price, and login-walled pricing rejected onto a flagged-sites sheet.

Outcome

What shipped

Three Excel reports per run: exact price matches sorted by total savings, alternate purchases driven by a client-editable equivalency table, and a full evidence audit carrying every URL, price, condition and rejection reason.

Runs as a CLI, as a FastAPI service, or through a React admin portal — upload, parse preview, processing, results, history and settings — with a dry-run mode that validates the parser against real orders without spending on APIs.

Next project

Dark Horse Safety

Want something like this?

Tell me what you're building and I'll tell you honestly how I'd approach it. Replies within hours.

Prefer email? Write to me directly at — it reaches the same inbox.

© 2026 Malaika AfridiXGitHubLinkedInUpworkEmail