Blog
How to Check Drug Interactions Programmatically: A Complete API Tutorial
Step-by-step tutorial for checking drug interactions via API. Complete code examples in cURL, Python, and JavaScript using RxLabelGuard's REST endpoints. Covers structured and natural language queries, response parsing, and production integration patterns.
Why check drug interactions via API
Medication errors involving drug-drug interactions remain one of the most common and preventable causes of adverse drug events. The Institute for Safe Medication Practices estimates that preventable adverse drug events affect over 1.3 million people annually in the United States alone. For healthcare software teams, building automated interaction checks into clinical workflows is not a nice-to-have feature; it is a baseline safety requirement.
Manual interaction checking does not scale. A pharmacist reviewing five concurrent medications faces ten possible pairwise combinations. At ten medications, that number grows to forty-five. At twenty medications, common among elderly patients with multiple chronic conditions, it reaches one hundred and ninety pairs. Programmatic checking via API is the only practical approach for handling this combinatorial complexity in real time.
Until recently, developers had limited options for programmatic interaction checking. The NLM Drug Interaction API was discontinued in January 2024. DrugBank's free Interaction Checker is retiring in March 2026. Commercial databases like First Databank and Micromedex require enterprise licensing and sales engagements. The openFDA drug label API provides raw data but requires building an entire extraction pipeline to produce structured results.
This tutorial walks through checking drug interactions programmatically using the RxLabelGuard API: a REST endpoint that accepts drug names and returns structured interaction data with severity scoring and FDA evidence citations. We will cover everything from your first API call to production integration patterns, with complete code examples in cURL, Python, and JavaScript.
What you need before starting
This tutorial assumes basic familiarity with REST APIs and HTTP requests. You will need a free RxLabelGuard account (no credit card required), which provides an API key and 50 requests per month on the Sandbox tier. If you plan to follow along with the code examples, you will need one of the following: a terminal with cURL installed (available by default on macOS and most Linux distributions), Python 3.7 or later with the requests library, or a JavaScript runtime such as Node.js 18 or later.
No healthcare domain knowledge is required. The API handles drug name resolution, label retrieval, interaction extraction, and severity classification. You send drug names as strings; you receive structured interaction data as JSON.
If you already have an account, you can skip ahead to the first API call. If not, the next section covers account setup.
Step 1: Create your free account
Navigate to the registration page at /register and create an account with your email address. After verifying your email, sign in and access your dashboard. The dashboard is where you manage API keys, monitor usage, and configure key-level restrictions.
From the dashboard, generate a new API key. Your key will have a prefix of rxlg_ followed by a random string. Copy this key and store it securely. You will include it as a header in every API request. The free Sandbox tier provides 50 requests per month, which is more than enough to complete this tutorial and build a working prototype.
A note on key security: treat your API key like a password. Do not commit it to source control, embed it in client-side code, or share it in public repositories. Use environment variables or a secrets manager in your application. The API key management documentation at /docs/authentication covers best practices in detail.
Step 2: Your first interaction check
The primary endpoint for checking drug interactions is POST /v1/interactions/check. It accepts a JSON body with a drugs array containing two or more drug names and returns structured interaction data. Let us start with a classic interaction pair: warfarin and aspirin.
Here is the cURL command:
curl -X POST https://api.rxlabelguard.com/v1/interactions/check -H "Content-Type: application/json" -H "x-api-key: YOUR_API_KEY" -d '{"drugs": ["warfarin", "aspirin"]}'
Replace YOUR_API_KEY with the key you generated in the previous step. If the request succeeds, you will receive a JSON response containing the drug resolutions, interaction pairs with severity scoring, and an FDA disclaimer. We will walk through this response structure in detail in the next section.
If you receive a 401 error, double-check that your API key is correct and that you are including it in the x-api-key header. If you receive a 429 error, you have exceeded your rate limit. The free tier allows 50 requests per month. See the rate limits documentation at /docs/rate-limits for details on limits by tier.
Step 3: Understanding the response
The response from the /check endpoint contains several top-level fields. Understanding each one is essential for integrating the data into your application.
The resolutions array contains one entry per drug you submitted. Each resolution includes the inputName (the string you sent), the resolved rxcui (RxNorm Concept Unique Identifier), the genericName, an array of brandNames, the splSetId (the stable identifier for the drug's FDA label), a labelUrl linking to the DailyMed source, and a resolved boolean indicating whether the drug was successfully identified. This resolution data lets you confirm that the API correctly identified the drugs you intended to check.
The pairs array is the core of the response. Each entry represents a pairwise interaction between two drugs. The drugA and drugB fields identify the pair. The maxSeverity field contains the highest severity level found across all interaction evidence for that pair, using one of five levels: contraindicated, major, moderate, minor, or unknown. The severity levels documentation at /docs/severity-levels explains each classification in detail.
Within each pair, the interactions array contains individual interaction records. Each record includes the source drug (whose label contained the interaction), the targetName, the severity level, the mechanism of interaction, a clinical recommendation, an evidenceSnippet (the exact text from the FDA label that supports the finding), the labelSection from which it was extracted (such as drug_interactions or contraindications), and the splSetId for traceability back to the source label.
The disclaimer field contains the FDA medical disclaimer that should be displayed to end users. The errors array will contain any issues encountered during processing, such as drugs that could not be resolved. The summary field is null when using the default structured format and is populated when you request summary or conversational format.
Python implementation
Here is a complete Python implementation using the requests library. This example includes error handling, response parsing, and severity-based filtering.
import requests import os import sys API_KEY = os.environ.get("RXLABELGUARD_API_KEY") BASE_URL = "https://api.rxlabelguard.com/v1" def check_interactions(drug_names, format_type="structured"): """Check drug interactions for a list of drug names.""" response = requests.post( f"{BASE_URL}/interactions/check", headers={ "Content-Type": "application/json", "x-api-key": API_KEY, }, json={ "drugs": drug_names, "format": format_type, }, timeout=30, ) response.raise_for_status() return response.json() def filter_by_severity(data, min_severity="moderate"): """Filter interaction pairs by minimum severity level.""" severity_order = [ "unknown", "minor", "moderate", "major", "contraindicated" ] min_index = severity_order.index(min_severity) return [ pair for pair in data["pairs"] if severity_order.index(pair["maxSeverity"]) >= min_index ] # Example usage if __name__ == "__main__": drugs = ["warfarin", "aspirin", "ibuprofen"] result = check_interactions(drugs) # Print resolution results for res in result["resolutions"]: status = "resolved" if res["resolved"] else "NOT FOUND" print(f"{res['inputName']} -> {res.get('genericName', 'N/A')} ({status})") # Print interactions filtered to moderate or higher serious = filter_by_severity(result, "moderate") for pair in serious: print(f"\n{pair['drugA']} + {pair['drugB']}: {pair['maxSeverity']}") for interaction in pair["interactions"]: print(f" Mechanism: {interaction['mechanism']}") print(f" Recommendation: {interaction['recommendation']}") print(f" Evidence: {interaction['evidenceSnippet'][:120]}...")
This implementation stores the API key in an environment variable rather than hardcoding it. The filter_by_severity function demonstrates how to work with the severity hierarchy to surface only clinically significant interactions. In a production application, you would likely add retry logic with exponential backoff, structured logging, and caching of results for repeated queries.
JavaScript implementation
Here is the equivalent implementation in JavaScript using the built-in fetch API, which is available in Node.js 18+ and all modern browsers.
const API_KEY = process.env.RXLABELGUARD_API_KEY; const BASE_URL = "https://api.rxlabelguard.com/v1"; async function checkInteractions(drugNames, format = "structured") { const response = await fetch(`${BASE_URL}/interactions/check`, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": API_KEY, }, body: JSON.stringify({ drugs: drugNames, format: format, }), }); if (!response.ok) { const error = await response.json().catch(() => ({})); throw new Error( `API error ${response.status}: ${error.message || response.statusText}` ); } return response.json(); } const SEVERITY_ORDER = ["unknown", "minor", "moderate", "major", "contraindicated"]; function filterBySeverity(data, minSeverity = "moderate") { const minIndex = SEVERITY_ORDER.indexOf(minSeverity); return data.pairs.filter( (pair) => SEVERITY_ORDER.indexOf(pair.maxSeverity) >= minIndex ); } // Example usage async function main() { const result = await checkInteractions(["warfarin", "aspirin", "ibuprofen"]); // Log resolutions for (const res of result.resolutions) { const status = res.resolved ? "resolved" : "NOT FOUND"; console.log(`${res.inputName} -> ${res.genericName ?? "N/A"} (${status})`); } // Filter and log serious interactions const serious = filterBySeverity(result, "moderate"); for (const pair of serious) { console.log(`\n${pair.drugA} + ${pair.drugB}: ${pair.maxSeverity}`); for (const interaction of pair.interactions) { console.log(` Mechanism: ${interaction.mechanism}`); console.log(` Recommendation: ${interaction.recommendation}`); } } } main().catch(console.error);
The JavaScript implementation follows the same pattern: make the request, parse the response, and filter by severity. For browser-based applications, remember that the API key should never be exposed in client-side code. Route API calls through your backend server to keep the key secure.
Checking multiple drugs at once
The /check endpoint accepts up to ten drugs in a single request. When you submit more than two drugs, the API performs pairwise comparison across all combinations. For example, submitting three drugs (warfarin, aspirin, and metformin) produces three pairwise checks: warfarin-aspirin, warfarin-metformin, and aspirin-metformin.
curl -X POST https://api.rxlabelguard.com/v1/interactions/check -H "Content-Type: application/json" -H "x-api-key: YOUR_API_KEY" -d '{"drugs": ["warfarin", "aspirin", "metformin", "lisinopril"]}'
With four drugs, the response contains up to six pairs. With ten drugs, it contains up to forty-five pairs. This pairwise approach ensures that no potential interaction is missed, even when one drug's label documents an interaction that the other drug's label does not mention. The API cross-references label text bidirectionally: it checks Drug A's label for mentions of Drug B and Drug B's label for mentions of Drug A.
Each pair in the response is independent, with its own maxSeverity and interactions array. This makes it straightforward to iterate through results and flag the most clinically significant findings. The resolutions array still contains one entry per input drug, regardless of how many pairs are generated.
Using the /ask endpoint for natural language
In addition to the structured /check endpoint, RxLabelGuard provides a natural language endpoint at POST /v1/interactions/ask. This endpoint accepts a free-text question and uses AI to extract drug names, run the interaction pipeline, and return results. It is documented in detail at /docs/ask.
curl -X POST https://api.rxlabelguard.com/v1/interactions/ask -H "Content-Type: application/json" -H "x-api-key: YOUR_API_KEY" -d '{"question": "Can I take warfarin with aspirin?", "format": "conversational"}'
The /ask endpoint is useful for patient-facing applications, chatbots, and any interface where users phrase questions naturally rather than submitting structured drug lists. The AI layer extracts drug names from the question, resolves them through the same RxNorm pipeline, and returns the same interaction data. When format is set to conversational, the summary field contains a plain-language explanation of the findings.
One important consideration: the /ask endpoint consumes the same rate limit quota as /check. It is not a separate allocation. For high-volume applications, the /check endpoint with structured drug lists is more efficient because it avoids the overhead of natural language parsing.
Response formats: structured vs summary vs conversational
The format parameter on both /check and /ask endpoints controls how results are returned. Understanding the three options helps you choose the right one for your use case.
The structured format (the default) returns the full resolutions array, pairs array with individual interaction records, and no summary text. This is the right choice for machine-to-machine integrations, EHR systems, clinical decision support engines, and any application that needs to programmatically process interaction data. The structured format provides maximum detail and is the most efficient to parse.
The summary format returns the same structured data plus a summary field containing an AI-generated overview of the findings. The summary is written in professional clinical language and is suitable for display to healthcare professionals. It highlights the most significant interactions, mentions severity levels, and references the underlying evidence. Use this format when you need both structured data for your application logic and a human-readable summary for display.
The conversational format also returns structured data plus a summary, but the summary is written in plain, accessible language suitable for patient-facing applications. It avoids jargon, explains interactions in practical terms, and emphasizes actionable recommendations like consulting a healthcare provider. Note that the Sandbox (free) tier supports only the structured format. The summary and conversational formats are available on the Developer ($20/month) and Professional ($99/month) tiers. See the pricing page at /pricing for full tier details.
Handling edge cases
Real-world drug inputs are messy. Users misspell drug names, submit brand names instead of generics, use abbreviations, or enter NDC codes. The API handles these cases through its RxNorm-based resolution layer, but understanding the behavior helps you build a better integration.
For misspellings, the API uses RxNorm's approximate matching to find the closest drug. Submitting 'warfrin' (missing the second a) will typically resolve correctly to warfarin. However, severely misspelled names may not resolve. When a drug cannot be resolved, its entry in the resolutions array will have resolved set to false, and you can prompt the user to correct the input. The drug resolution documentation at /docs/drug-resolution explains the full resolution cascade.
For brand names, the API resolves them to their generic equivalent. Submitting 'Coumadin' resolves to warfarin, and 'Advil' resolves to ibuprofen. The resolutions array will show both the inputName (what the user submitted) and the genericName (what it resolved to), along with the brandNames array for reference.
For rate limit errors (HTTP 429), the response includes a Retry-After header indicating how many seconds to wait before retrying. Implement exponential backoff in your retry logic. If you consistently hit rate limits, upgrading to the Developer or Professional tier increases your allocation to 2,000 or 20,000 requests per month respectively, with overage pricing available for bursts. Rate limit details are documented at /docs/rate-limits.
For drugs with no documented interactions in their FDA labeling, the pairs array for that combination will be empty or contain entries with severity set to unknown. An empty pairs result does not necessarily mean the drugs are safe to combine; it means the FDA label for those drugs does not document an interaction. The disclaimer field in every response reminds consumers of this distinction.
Integrating into your application
How you integrate the API depends on your application type. Here are patterns for common use cases.
For EHR integrations, the typical pattern is a synchronous check triggered when a clinician prescribes a new medication. Your EHR system submits the new drug along with the patient's current medication list to the /check endpoint. The response is filtered by severity (typically showing major and contraindicated interactions as hard stops, moderate interactions as warnings, and minor interactions as informational notes). The evidence citations from each interaction record provide the clinical justification needed for informed decision-making. See our EHR integration guide at /use-cases/ehr-integration for more detailed patterns.
For pharmacy systems, batch checking is common. When a patient presents a new prescription, the system checks it against their medication profile. The multi-drug /check endpoint handles this in a single API call. Pharmacy workflows often need to log the interaction check results for regulatory compliance, making the splSetId and labelSection fields in each interaction record particularly valuable for audit trails.
For patient-facing applications, the /ask endpoint with conversational format provides the most accessible output. However, patient-facing applications carry additional responsibility around presentation. The disclaimer must be prominently displayed. Results should emphasize consulting a healthcare provider rather than making medication decisions based solely on API output. Our free drug interaction API page at /free-drug-interaction-api covers the free tier capabilities suitable for patient-facing prototypes.
Next steps
You now have a working understanding of how to check drug interactions programmatically using the RxLabelGuard API. From here, several resources will help you build a production integration.
The full API documentation at /docs covers authentication, both endpoints, response schemas, error codes, and rate limits in detail. The check interactions endpoint reference at /docs/check-interactions and the ask endpoint reference at /docs/ask provide complete parameter documentation with additional examples.
For evaluating how RxLabelGuard compares to other options, our comparison page at /compare provides a side-by-side analysis against DrugBank, openFDA (DIY), First Databank, and other alternatives. If you are considering building your own pipeline on openFDA data, our guide on the openFDA drug label API at /blog/openfda-drug-label-api-developer-guide covers the raw data layer in depth.
When you are ready to move beyond the free tier, the pricing page at /pricing breaks down all three tiers with exact request limits, API key allowances, response format availability, and overage pricing. The Developer tier at $20 per month provides 2,000 requests and unlocks all response formats, which is sufficient for most development and staging environments.
Create your free account at /register and start checking drug interactions in minutes. No credit card required.
References
- openFDA Drug Label Endpoint (U.S. Food and Drug Administration (FDA); accessed Mar 6, 2026)
- RxNorm API (U.S. National Library of Medicine (NLM); accessed Mar 6, 2026)
- RxNav APIs (U.S. National Library of Medicine (NLM); accessed Mar 6, 2026)