API Reference
REST API
A structured, versioned API for querying the full TwinFyRx drug intelligence platform. All endpoints return JSON and follow consistent patterns for pagination, errors, and resource nesting.
Authentication & Access Tiers
TwinFyRx uses a tiered access model. During the evaluation period, data endpoints are openly accessible — no key required. Once a license is activated per customer, those endpoints require an X-API-Key header. A wrong key always returns 401.
| Surface | Access | Paths |
|---|---|---|
| Always public | No key, ever | /v1/demo/* /v1/data-quality/* /v1/metadata/* /v1/fhir/* /health* |
| Licensed surfaces | Open now; key-gated once license is active | /v1/drugs/* /v1/pricing/* /v1/formulary/* /v1/diseases/* /v1/ndc/* /v1/atc/* /v1/medicare/* /v1/pharmacies/* |
Once you have a key, include it on every request to the licensed surfaces:
curl -H "X-API-Key: your_api_key" \ https://api.twinfyrx.com/v1/drugs/search?q=atorvastatin
Key provisioning: One key per customer, scoped to the licensed tier. Keys are issued during onboarding and rotated on request. Request access to get started.
Backwards compatibility: The drug_id primary key, all enum values, and the v1 path prefix are stable. Breaking changes will be versioned under a new path prefix with a minimum six-month migration window.
Base URL
https://api.twinfyrx.com
All endpoints are versioned under /v1.
Response Headers
Every response carries three diagnostic headers:
| Header | Value | Notes |
|---|---|---|
| X-Schema-Version | 1.0 | Schema version in effect. Increments on breaking changes. |
| X-Data-As-Of | 2026-04-28T14:32:00Z | UTC timestamp of the most recent data sync to MotherDuck. |
| X-Request-ID | uuid-v4 | Echo of client-supplied X-Request-ID, or a server-generated UUID. Include in support tickets. |
Supply your own X-Request-ID on outbound calls to correlate requests across your own logs. X-Schema-Version and X-Data-As-Of are exposed via CORS so browser clients can read them.
Error Handling
All error responses follow a consistent structure. The request_id field matches the X-Request-ID header and is the primary reference for support:
{
"error": {
"code": "NOT_FOUND",
"message": "Drug with rxcui 999999 not found",
"request_id": "a3f2c1d8-7b4e-4e2a-9c1f-0d5e8a3b2f91"
}
}| Status | Code | Meaning |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key |
| 404 | NOT_FOUND | Resource not found |
| 422 | VALIDATION_ERROR | Invalid query parameters |
| 500 | INTERNAL_ERROR | Unexpected server error |
Drugs
Search, retrieve, and explore the drug concept hierarchy. Every response anchors to a canonical RxNorm drug concept.
/v1/drugs/searchSearch drugs by name, ingredient, or brand name. Returns paginated results.
Parameters
qstringREQUIREDSearch query (min 2 characters)limitintegerResults per page (1–100, default 20)offsetintegerPagination offsetResponse
results[]DrugSummaryArray of matching drugs.rxcuiintegerRxNorm concept ID (drug_id).drug_namestringFull clinical drug name.ingredientstringActive ingredient(s).is_genericbooleanWhether this is a generic concepttotalintegerTotal matching resultsExample Response
{
"results": [
{
"rxcui": 617314,
"drug_name": "atorvastatin 20 MG Oral Tablet",
"ingredient": "atorvastatin",
"dose_form": "Oral Tablet",
"clinical_drug_form": "atorvastatin Oral Tablet",
"brand_name": null,
"is_generic": true
}
],
"total": 12,
"limit": 20,
"offset": 0
}/v1/drugs/{rxcui}Full drug detail including RxNorm hierarchy, therapy attributes, and branded variants.
Parameters
rxcuiintegerREQUIREDRxNorm concept ID (path parameter)Response
rxcuiintegerDrug concept IDdrug_namestringFull drug nameingredient_rxcuiintegerIngredient concept IDclinical_drug_form_rxcuiintegerDrug form concept IDtherapy_patternstringTherapy behavior enumspecialty_flagbooleanSpecialty drug indicatorbenefit_channelstringPharmacy, medical, or dual/v1/drugs/{rxcui}/icd10-volumesUtilization-weighted drug-to-diagnosis mappings with percent volume estimates.
Response
drugDrugSummaryDrug summary objectis_single_indication_drugbooleanWhether drug maps to a single diagnosisicd10_volumes[]ICD10VolumeDiagnoses sorted by volume.icd10_codestringICD-10-CM code.pct_volumedecimalEstimated % of prescriptions for this diagnosis.is_dominant_indicationbooleanWhether this is the primary indication/v1/drugs/{rxcui}/suspecting-rulesAll active suspecting rules for a drug. Each rule contains structured JSON logic for execution.
Response
drugDrugSummaryDrug summaryrule_countintegerTotal rules returnedrules[]SuspectingRuleArray of executable rules.rule_categorystringprescriber_specialty, concurrent_rx, compound, etc..rule_strengthstringstrong_prior, weak_prior, or exclusion.rule_logicobjectStructured logic for SQL generation.confidencedecimalBase confidence score (0–1)Pricing
NADAC acquisition costs (weekly cadence) and Medicare Part B ASP payment limits (quarterly). Both resolve through the drug concept hierarchy.
/v1/drugs/{rxcui}/pricingCurrent pricing snapshot — latest NADAC week and latest ASP quarter for a drug.
Response
drugDrugSummaryDrug summarynadacNADACSnapshot | nullLatest NADAC pricing (if available).median_per_unitdecimalMedian acquisition cost per unit.pricing_unitstringUnit label (e.g., 'EACH', 'ML')aspASPSnapshot | nullLatest ASP pricing (if available).median_payment_limitdecimalMedian Part B payment limit/v1/drugs/{rxcui}/nadacHistorical NADAC pricing with weekly granularity. Defaults to 2 years of history.
Parameters
startdateStart date (YYYY-MM-DD, default: 2 years ago)enddateEnd date (YYYY-MM-DD, default: today)Response
pricing_unitstringUnit of measureweeks[]NADACWeekWeekly pricing data points.as_of_datedateEffective date.median_per_unitdecimalMedian cost per unit.ndc_countintegerNDCs contributing to this aggregateExample Response
{
"drug": { "rxcui": 617314, "drug_name": "atorvastatin 20 MG Oral Tablet" },
"pricing_unit": "EACH",
"weeks": [
{ "as_of_date": "2026-03-05", "median_per_unit": 0.0412, "min_per_unit": 0.0389, "max_per_unit": 0.0445, "ndc_count": 14 },
{ "as_of_date": "2026-02-26", "median_per_unit": 0.0415, "min_per_unit": 0.0391, "max_per_unit": 0.0448, "ndc_count": 14 }
]
}/v1/drugs/{rxcui}/aspHistorical ASP (Medicare Part B) payment limits by quarter with HCPCS code mappings.
Response
quarters[]ASPQuarterQuarterly ASP data points.effective_quarterstringe.g., '2026Q1'.median_payment_limitdecimalMedian payment limit per unit.hcpcs_codesstring[]Associated HCPCS codesDiagnoses
Search and explore ICD-10-CM diagnoses with reverse drug mappings — find which drugs are most commonly prescribed for a given condition.
/v1/diagnoses/searchSearch ICD-10 diagnoses by code or description text.
Parameters
qstringREQUIREDICD-10 code or description textlimitintegerResults per page (default 20)Response
results[]DiseaseSummaryMatching diagnoses.icd10_codestringICD-10-CM code.diagnosisstringDiagnosis description.is_billablebooleanWhether this is a billable code/v1/diagnoses/{icd10_code}Full diagnosis detail including ICD-10 hierarchy and top 20 drugs prescribed for this condition.
Response
icd10_codestringICD-10-CM codediagnosisstringFull descriptionchapter_namestringICD-10 chapteris_billablebooleanBillable indicatortop_drugs[]DrugForDiseaseTop 20 drugs by prescribing volume.pct_volumedecimalEstimated share of prescriptionsClassification & Identifiers
ATC therapeutic classification hierarchy and NDC identifier resolution.
/v1/atc/searchSearch ATC classification codes by code or name.
Parameters
qstringREQUIREDATC code or therapeutic class name/v1/atc/{atc_code_5}Full 5-level ATC hierarchy for a given code.
Response
atc_code_1 → atc_code_5stringAll 5 ATC hierarchy levelsatc_name_1 → atc_name_5stringNames for each level/v1/drugs/{rxcui}/atcAll ATC classifications for a drug concept.
/v1/ndc/{ndc_code}Resolve an NDC code to its drug concept with therapy attributes and specialty flags.
Response
ndcstringInput NDC coderxcuiintegerResolved drug concept IDdrug_namestringDrug nametherapy_patternstringTherapy behavior classificationspecialty_flagbooleanSpecialty drug indicatorPagination
List endpoints return a consistent envelope. The response also carries an X-Total-Count header with the untruncated count, useful for building UI paginators without parsing the body:
{
"results": [ ... ],
"total": 142,
"limit": 20,
"offset": 0
}
# Response header:
X-Total-Count: 142| Rule | Detail |
|---|---|
| Max limit | 1000 results per request. Default is 20. |
| Offset paging | Use limit + offset for result sets up to ~10,000 rows. |
| Bulk endpoint | POST /v1/drugs:lookup accepts up to 500 rxcuis per call — preferred over looping single-drug GETs. |
Rate Limits
Rate limits are enforced per API key. Specific limits are communicated during onboarding and vary by tier. When a limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header indicating how many seconds to wait before retrying.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-Request-ID: a3f2c1d8-7b4e-4e2a-9c1f-0d5e8a3b2f91
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Retry after 30 seconds.",
"request_id": "a3f2c1d8-7b4e-4e2a-9c1f-0d5e8a3b2f91"
}
}Recommended retry strategy: exponential backoff starting at 1 second, capped at 60 seconds, with jitter. For batch workloads, use POST /v1/drugs:lookup to reduce request volume — 500 drugs in one call instead of 500 individual GETs.