Skip to content
Adarsh Jha
All projects
Data engineering · 2026

MedSwitch

Finds a cheaper, chemically identical substitute for Indian prescriptions

₹5,684

annual saving on one real prescription

TypeScriptPostgreSQLDrizzlepgvectorBright DataOpenAI

Overview

Compares pharmacy prices and compositions across Indian retailers so a patient on a chronic medication can find an equivalent that costs a fraction of the brand — and flags anything on the CDSCO banned list along the way.

The problem

Someone on a long-term prescription in India can be paying ten times what the identical composition costs at a different retailer, and there is no way to check without knowing the chemistry. The information is public and spread across sites that describe the same drug in incompatible ways.

Approach

Product listings are ingested from four public sources into a re-parseable raw store, so a parser bug is a re-run rather than a re-scrape. Each retailer's composition text is parsed into structured molecule, strength, dosage form and release modifier by whichever parser matches the grammar it actually uses — one regex handles PharmEasy's rigid strings, a Zod-validated batched LLM parser handles Jan Aushadhi's free text, cached by hash so a re-run only pays for genuinely new strings. Molecule resolution tries exact match, then an alias table, then salt-suffix stripping, before auto-creating a new molecule as a last resort. Equivalents converge on a composition fingerprint, and substitution queries compare real price-per-unit.

What it does

  • Resolves molecules across retailers through synonyms and salt-form differences, then matches equivalents by composition fingerprint
  • A real prescription resolves to the same composition at ₹17.09 and ₹1.51 per tablet — about ₹5,684 a year at one tablet daily
  • Hybrid parsing: regex for rigidly structured retailers, batched LLM for free text, cached by hash — roughly 40% of rows parse with zero LLM calls
  • Every scraped composition checked against the CDSCO list of banned fixed-dose combinations
  • Nearest Jan Aushadhi store ranked by real haversine distance rather than a pincode-string guess

What I learned

Extraction gaps had to become visible rather than silent. Apollo's extraction is noticeably less reliable under batch scraping — most likely anti-bot rate limiting rather than a collector bug — and the useful move was logging every gap as an `extraction_issue` row instead of dropping it. A pipeline that quietly loses 20% of its rows looks exactly like one that works.