Brand ⇄ generic drug-name converter — dosage, warnings and side effects from official FDA & UK labels, AI-summarised, with a side-by-side US vs UK comparison.
Live demo · Quick start · API · Architecture · Roadmap · Contributing
Drug names are a two-language problem: patients and marketing speak brand ("Lipitor"), while prescribing information, research and pharmacists speak generic ("atorvastatin"). Pharmonym translates between the two — and brings the official label along for the ride.
The design principle throughout: authoritative data first, AI only to summarise it — never to generate clinical facts.
- 🔁 Brand ⇄ generic, both directions — deterministic resolution via RxNorm/RxNav with an openFDA fallback. No hallucinated drug names.
- 🏷️ Official label data — dosage, warnings, contraindications and side effects from the US FDA label (openFDA, cited to DailyMed) and the UK/EU SmPC (eMC).
- 🇺🇸🆚🇬🇧 US vs UK comparison — side-by-side differences between the two regulatory labels for the same drug.
- ✨ Grounded AI summaries — one model call condenses the official label text into "at a glance" lines; the model is instructed to use only the supplied text.
- 🚦 Production hardening — Firestore caching (30 days), per-IP rate limiting, CORS allowlist, bounded scale-out.
- 📦 Open engine — name resolution and label parsing live in
@pharmatools/drug-data, shared with PubCrawl.
Try the hosted API — 10 seconds:
curl -X POST https://us-central1-rx-converter.cloudfunctions.net/convertDrugName \
-H "Content-Type: application/json" \
-d '{"name": "Lipitor"}'Run it locally — under 60 seconds:
git clone https://github.com/nickjlamb/pharmonym.git
cd pharmonym/functions && npm install
npx firebase-tools emulators:start --only functionsNo API keys needed for the deterministic core (name resolution + label fetch).
To enable the AI summaries and last-resort fallback locally, add your key to
functions/.secret.local (gitignored):
echo "OPENAI_KEY=sk-..." >> .secret.local| Endpoint | https://us-central1-rx-converter.cloudfunctions.net/convertDrugName |
| Body | { "name": "<brand or generic drug name>" } |
| Browser calls | Allowed from the CORS allowlist in functions/index.js |
| Server-to-server | Allowed (requests without an Origin header) |
The response is an OpenAI-chat-completion-style envelope (a stable contract
with the widget): parse choices[0].message.content as JSON.
| Status | Meaning |
|---|---|
400 |
Missing or implausible drug name |
403 |
Browser origin not on the allowlist |
405 |
Non-POST method |
429 |
Rate limit: 30 uncached requests per IP per hour (Retry-After: 3600) |
500 |
Conversion failed |
Cached responses (hits within 30 days for label-bearing results, 1 day for label-less ones) don't count against the rate limit.
Generic → brand:
curl -X POST https://us-central1-rx-converter.cloudfunctions.net/convertDrugName \
-H "Content-Type: application/json" \
-d '{"name": "semaglutide"}'From JavaScript:
const res = await fetch(
"https://us-central1-rx-converter.cloudfunctions.net/convertDrugName",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Humira" }),
}
);
const envelope = await res.json();
const drug = JSON.parse(envelope.choices[0].message.content);
console.log(`${drug.inputName} → ${drug.genericName}`); // Humira → AdalimumabEmbed the widget: pharmonym.html is a self-contained embed (currently
hosted in Webflow at pharmatools.ai/pharmonym) —
drop it into any page and point it at your own deployment of the function.
| Path | Role |
|---|---|
functions/index.js |
Cloud Functions entry: convertDrugName, clearCache (admin), scheduled cache cleanup, rate limiting, CORS |
functions/resolveName.js |
Name resolution — thin shim over @pharmatools/drug-data |
functions/labels.js |
US (openFDA/DailyMed) + UK (eMC SmPC) label fetch — shim over the shared engine |
functions/summarise.js |
Grounded AI summaries: at-a-glance + US/UK differences |
pharmonym.html |
Self-contained front-end widget |
pharmonym-jsonld.html |
JSON-LD structured data for the live page |
| Source | Used for |
|---|---|
| RxNorm / RxNav | Name mapping, drug class, indications |
| openFDA | US label data |
| DailyMed | US label citations |
| eMC | UK/EU SmPC label data |
Directional and open to input — open an issue to influence it.
- Automated test suite (parser fixtures for openFDA + eMC sections)
- Additional regulators: EMA and Health Canada labels
- Batch conversion endpoint (
names: []) - More label sections: interactions, pregnancy & lactation
- TypeScript migration of the Cloud Functions
- Continue consolidating label logic into
@pharmatools/drug-data
PRs and issues welcome — see CONTRIBUTING.md for setup, style, and the one non-negotiable rule (AI never generates clinical facts). Releases are documented in the CHANGELOG.
MIT © Nick Lamb
"At a glance" summaries are AI-generated from official prescribing information and are not a substitute for professional medical advice. Always verify against the full prescribing information or ask a pharmacist.