By the fastCRW team · Catalogue figures checked 2026-11-05 against GET /v1/data · Verify independently before relying on any figure.
The openFDA API returns adverse event reports, drug labels and recalls as JSON. The ClinicalTrials.gov API v2 returns registered studies as JSON. Both are public US government sources, and both have their own query syntax and paging. Through the Pergamon catalogue, one API key reaches both, plus RxNorm for drug names and the NPI Registry for providers. Every response carries the source name, the licence text and the attribution line.
What each source answers
| Source | Use it for | Capabilities used here |
|---|---|---|
| openFDA | Adverse event reports, drug labels, recalls | drugevent, druglabel, drugrecall |
| ClinicalTrials.gov | Studies by condition, one study by NCT ID | studies, study |
| RxNorm | Turn a drug name into an RxCUI code | rxcui-by-name, all-related |
| NPI Registry | Look up a US healthcare provider by NPI number | number |
DailyMed is also in the catalogue, but it runs on your machine through the local crw-mcp, so its calls cost 0 credits and the data never passes through fastCRW. Its catalogue row says: "It is your responsibility to determine and satisfy copyright or other use restrictions when using materials that are not in the public domain."
Search openFDA adverse event reports
openFDA takes a search string in field:value form. Pass it as an option on the Pergamon call and the response is the upstream openFDA body inside the usual envelope. This call asks for three drug event reports that mention metformin:
curl "https://api.fastcrw.com/v1/data/openfda/drugevent?search=patient.drug.medicinalproduct:metformin&limit=3" \
-H "Authorization: Bearer $CRW_API_KEY"
Output from our run on 2026-11-05, with the results array cut down to its shape:
{
"success": true,
"provider": "openfda",
"capability": "drugevent",
"credits_used": 1,
"data": {
"meta": { "...": "...", "results": { "skip": 0, "limit": 3, "total": 439327 } },
"results": [ ... ]
}
}
The count option turns the same search into a tally. This one counts the reactions reported alongside metformin:
curl "https://api.fastcrw.com/v1/data/openfda/drugevent?search=patient.drug.medicinalproduct:metformin&count=patient.reaction.reactionmeddrapt.exact&limit=3" \
-H "Authorization: Bearer $CRW_API_KEY"
"results": [
{ "term": "NAUSEA", "count": 30628 },
{ "term": "DIARRHOEA", "count": 28491 },
{ "term": "BLOOD GLUCOSE INCREASED", "count": 27306 }
]
That number is a count of matching reports. It is not a rate of harm, and it does not say how many people took the drug.
Recalls and drug labels
Recalls use the same search syntax on drugrecall. Each record carries recall_number, reason_for_recall, recalling_firm, status and recall_initiation_date. A search on product_description:metformin returned 91 recall records in our run.
curl "https://api.fastcrw.com/v1/data/openfda/drugrecall?search=product_description:metformin&limit=2" \
-H "Authorization: Bearer $CRW_API_KEY"
Drug labels come from druglabel. Search on openfda.generic_name:metformin to get the label text for one generic name:
curl "https://api.fastcrw.com/v1/data/openfda/druglabel?search=openfda.generic_name:metformin&limit=1" \
-H "Authorization: Bearer $CRW_API_KEY"
In our run the search matched 350 labels. Each result is one label split into sections, with keys such as boxed_warning, indications_and_usage, dosage_and_administration, contraindications and adverse_reactions, plus an openfda object with the generic and brand names. The first label in that run was for a sitagliptin and metformin product, so filter on the exact generic name if you want single-ingredient labels only.
Trials by condition with the Python SDK
The ClinicalTrials.gov studies capability takes query fields such as query.cond and pageSize. The SDK returns the upstream body under data:
# pip install crw
from crw import CrwClient
client = CrwClient() # reads CRW_API_KEY
result = client.get_data("clinicaltrials", "studies", {"query.cond": "type 2 diabetes", "pageSize": 3})
print(result["provenance"]["attributionText"])
for study in result["data"]["studies"]:
print(study["protocolSection"]["identificationModule"]["nctId"])
Output from our run: Source: ClinicalTrials.gov, then the NCT IDs NCT03315780, NCT07843862 and NCT01046994. Your run will return a different list as studies are added.
One study by NCT ID: always send fields
A full study record is large. The record NCT04280705 is 213,424 bytes on the REST response. Ask for the fields you need with fields:
curl "https://api.fastcrw.com/v1/data/clinicaltrials/study?nct=NCT04280705&fields=NCTId,BriefTitle,OverallStatus" \
-H "Authorization: Bearer $CRW_API_KEY"
The data field from that call is:
{"protocolSection":{"identificationModule":{"nctId":"NCT04280705","briefTitle":"Adaptive COVID-19 Treatment Trial (ACTT)"},"statusModule":{"overallStatus":"COMPLETED"}}}
An agent's context window is the reason to do this. Over MCP, a result above 256 KiB is refused with result_too_large, so ask for fields on big records.
Normalize drug names with RxNorm
Drug names vary by brand, spelling and salt form. RxNorm maps a name to an RxCUI, a stable code you can join on. Look up the code first:
curl "https://api.fastcrw.com/v1/data/rxnorm/rxcui-by-name?name=metformin" \
-H "Authorization: Bearer $CRW_API_KEY"
{"idGroup":{"rxnormId":["6809"]}}
Then ask for everything related to that code, such as brand names and ingredient groups:
curl "https://api.fastcrw.com/v1/data/rxnorm/all-related?rxcui=6809" \
-H "Authorization: Bearer $CRW_API_KEY"
In our run this returned 19 concept groups for RXCUI 6809. A name can map to more than one code, so check the list before you join records on it.
Look up a provider by NPI
The NPI Registry number capability takes a 10-digit NPI and returns the matching record:
curl "https://api.fastcrw.com/v1/data/npi/number?number=1003000126" \
-H "Authorization: Bearer $CRW_API_KEY"
In our run the response had result_count: 1 and a first taxonomy of "Internal Medicine", with a state of MD in the address. Read the full record before you use it for a decision about a specific clinician.
Let the agent find the right call
Discovery is free. This find call returned 110 matching capabilities for "adverse events", with the first three being openfda/drugevent, openfda/foodevent and openfda/deviceevent, each at 1 credit:
curl "https://api.fastcrw.com/v1/data/find?query=adverse%20events&limit=3" \
-H "Authorization: Bearer $CRW_API_KEY"
An MCP client makes the same two steps as tool calls. Discover the capability, then call it with its options:
{"name": "crw_find_data", "arguments": {"query": "adverse events", "limit": 3}}
{"name": "crw_get_data", "arguments": {"provider": "openfda", "capability": "drugevent", "options": {"search": "patient.drug.medicinalproduct:metformin", "limit": 3}}}
What it costs
Find and catalogue calls are free. Each server call in this post costs 1 credit, and a failed call is not billed. DailyMed runs on your machine and costs 0 credits. See pricing for plan prices.
What this does not do
- It is not medical advice. The openFDA licence text in every response reads: "openFDA adds its own terms: the data is not for clinical decisions and you may not imply FDA endorsement." Keep that line in any product built on this data.
- An adverse event report is a submitted report. It shows that an event was reported, not that the drug caused it.
- A count is not an incidence rate. The tallies above count matching reports only.
- Drug names are messy. Check the RxNorm concept groups before you join records on a code.
- Trial records change. Read the registry record itself before you act on a trial's status.
Read the provenance before you use the data
Every REST response has the same outer shape, and the provenance block tells you where the data came from. For the openFDA call above, the block names the upstream URL that was fetched, so you can open the same query at the source and compare:
"provenance": {
"source": "openFDA",
"upstream_url": "https://api.fda.gov/drug/event.json?search=patient.drug.medicinalproduct%3Ametformin&limit=3",
"licence": "A work of the United States Government, not subject to domestic copyright under 17 U.S.C. 105, published by the agency as open data. openFDA adds its own terms: ...",
"licence_url": "https://open.fda.gov/terms/",
"attribution_text": "Source: openFDA, U.S. Food and Drug Administration",
"retrieved_at": "...",
"cache_age_seconds": 0,
"is_stale": false
}
The is_stale flag is true when the body was served from fastCRW's response cache. cache_age_seconds then says how old the cached copy is. It does not mean the upstream data is out of date, so do not treat it as a freshness warning on its own.
Errors you will see
- Unknown capability. A wrong provider or capability name returns HTTP 404 with
errorCodeunknown_data_source. Runfindto get the exact names. - Upstream failure. When openFDA or another source fails, the response has
success: falseanderrorCodeupstream_error, orupstream_timeoutfor a timeout. A failed call is not billed. - Client-run source over REST. DailyMed runs in the local
crw-mcp. Itsdrug-namescapability called over REST returns HTTP 409 withclient_execution_required, and the error text names the install command. The same call works from an MCP client that has the local server installed.
Quote phrases inside the search value
openFDA matches a phrase when you put it in double quotes inside the search value. In the URL the quotes are percent-encoded as %22. This call returned a result in our run:
curl "https://api.fastcrw.com/v1/data/openfda/drugevent?search=patient.drug.medicinalproduct:%22metformin%22&limit=1" \
-H "Authorization: Bearer $CRW_API_KEY"
Use the same pattern for other fields. Check the field name in the openFDA documentation first, because a wrong field name is an upstream error, not an empty answer.
A workflow across the four sources
A drug-safety agent usually runs the same four steps. Each one narrows the next:
- Normalize the name. Call
rxnorm/rxcui-by-namewith the name the user typed. In our run "metformin" returned RXCUI 6809. - Widen with related names. Call
rxnorm/all-relatedwith that RXCUI, and add the brand and ingredient names to your search terms. - Read the reports and labels. Run
openfda/drugeventwithcountfor an overview, thendrugrecallanddruglabelfor the record-level detail. Keep the query string in your output so a reviewer can rerun it. - Find the trials. Call
clinicaltrials/studieswithquery.condfor the condition, andclinicaltrials/studywithfieldsfor each NCT ID you need to read.
Every call is 1 credit, so a pass over one drug is a handful of credits you can count before you start.
Sources
- openFDA API documentation
- openFDA terms
- ClinicalTrials.gov API v2 documentation
- RxNav API documentation
- NPI Registry API page
Related: PubMed API for AI agents · Public data API for AI agents · Pergamon catalogue
