openfda-mcp-server
An MCP server that provides 15 tools to query the openFDA public API. It allows agents to search adverse events, recalls, drug approvals, and device clearances, look up NDC codes and drug labels, and perform SQL analysis on staged data via DataCanvas.
Endpoint and access
No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.
https://openfda.caseyjhand.com/mcpWhat its tools do
14 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.
Read-only (14)
openfda_search_adverse_eventsopenfda_search_animal_eventsopenfda_search_drug_shortagesopenfda_search_recallsopenfda_search_tobacco_reportsopenfda_count_valuesopenfda_describe_fieldsopenfda_get_drug_labelopenfda_search_drug_approvalsopenfda_search_device_clearancesopenfda_lookup_ndcopenfda_drug_profileopenfda_dataframe_describeopenfda_dataframe_query
Checks and signals
Each line is separate evidence, not a trust score.
- Passed Endpoint answered the live check ·
- Availability 1 of 1 checks passed in 30 days
What you can do with it
- Search adverse event reports across drugs, food, and devices
- Look up FDA drug labeling and package inserts
- Search enforcement reports and recall actions
- Run read-only SQL over staged FDA result sets
Source and maintenance
- Maintenance checked
Protocol and capabilities
- Server
- openfda-mcp-server 0.9.1
- MCP protocol negotiated in check
- 2025-11-25
- Tools
- 14 advertised
- Resources
- 1 advertised
- Resource templates
- 1 advertised
- Prompts
- 0 advertised
{
"tools": {
"listChanged": true
},
"logging": {},
"prompts": {
"listChanged": true
},
"resources": {
"subscribe": true,
"listChanged": true
},
"extensions": {
"io.modelcontextprotocol/skills": {
"directoryRead": true
}
}
}Server-provided instructions
Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.
Tools
14 advertised
openfda_search_adverse_events — Openfda Search Adverse Events
Search adverse event reports across drugs, food, and devices. Use to investigate safety signals, find reports for a specific product, or explore reactions by demographics. With stage=true, call openfda_dataframe_describe for the staged columns, then openfda_dataframe_query for SQL.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"category"
],
"properties": {
"skip": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Number of records to skip for pagination (default 0). openFDA caps pagination at 25000 records; a higher value returns a pagination_limit_reached error."
},
"sort": {
"type": "string",
"pattern": "^\\s*(?=\\S)(?![,:.])(?!.*,(?: *[,:.]|\\s*$))(?!.*[^\\s,] +[^\\s,])(?!.*\\.(?:[.:,\\s]|$))(?!.*:(?!(?:[Aa][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?|[Dd][Ee][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?)(?: *,|\\s*$)))[A-Za-z0-9_.:, ]*[A-Za-z0-9_]\\s*$",
"minLength": 1,
"description": "Sort expression — a field path optionally suffixed with :asc or :desc (any letter case; :ascending and :descending are also accepted, and every direction is sent as asc or desc); comma-separate for multi-field sort. Field paths take only letters, digits, underscores, and dots; any other character or direction is rejected before the request. Sortable date fields are category-specific: drug → receivedate:desc (or receiptdate), food → date_created:desc (or date_started), device → date_received:desc (or date_of_event). A field from another category (e.g. receivedate on food or device) causes a query error — use the field for this category."
},
"limit": {
"type": "number",
"default": 10,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1-1000, default 10). Serialized record size varies by three orders of magnitude across openFDA endpoints, so the page is also bounded by a 24000-byte serialized budget: a page that would overrun it returns fewer records than requested and reports the cut on page_omitted. Whenever any record matched, at least one comes back, however large it measures — page_bytes then reports its size and page_largest_fields names its largest fields, which fields can project away. Drug reports are by far the largest — a single drug/event report averages tens of kilobytes where a food/event report is a few hundred bytes."
},
"stage": {
"type": "boolean",
"default": false,
"description": "Stage the matched set on a DataCanvas for SQL analysis — openfda_dataframe_describe lists the staged columns, openfda_dataframe_query runs the SQL. Default false — the call returns one page for one upstream request. When true, records are also drained onto a canvas table up to a size budget (staged_rows reports how many reached it). Staging is for record-level SQL over a bounded slice; for a distribution over everything that matched, openfda_count_values aggregates server-side in one request. Requires CANVAS_PROVIDER_TYPE=duckdb."
},
"fields": {
"type": "array",
"items": {
"type": "string",
"pattern": "^(?!\\.)(?!.*\\.\\.)(?!.*\\.$)[A-Za-z0-9_.]+$"
},
"description": "Dotted record paths to keep on each returned record, e.g. [\"receivedate\", \"patient.drug.medicinalproduct\"] — any key path on a returned record works, and openfda_describe_fields lists the searchable ones. Each record is projected to these paths plus its identity key (safetyreportid, report_number, mdr_report_key) before the inline byte budget is measured, identically on both response surfaces, and a path through an array applies to every element. Use it when records are large: a page_bytes over the budget comes with the largest fields of the oversized record. A path no returned record carries is named on the notice. Omit for whole records. Paths take only letters, digits, underscores, and dots; anything else is rejected before the request. With stage=true, only the inline page is projected — the staged table keeps its columns."
},
"search": {
"type": "string",
"pattern": "\\S",
"minLength": 1,
"description": "openFDA search query. Examples: patient.drug.medicinalproduct:\"aspirin\", patient.reaction.reactionmeddrapt:\"nausea\" AND serious:\"1\". Omit to browse recent. Separate clauses with spaces. A \"+\" written in the URL style of openFDA's examples also reads as a space where it separates: beside AND, OR, NOT, or TO, after a closing quote, paren, or bracket, or between a term and the next \"field:\" prefix (a:\"x\"+AND+b:\"y\", [20200101+TO+20201231], c:x+d:y). In a query that marks a clause required with a leading +field:, only the AND/OR/NOT/TO positions read that way. Any other \"+\" is sent as written, and inside a quoted phrase \"+\" is a literal plus sign. Double quotes, parentheses, and range brackets must balance, and the query must not end on a backslash — each is rejected before the request."
},
"category": {
"enum": [
"drug",
"food",
"device"
],
"type": "string",
"description": "Product category — each has different field schemas in the response"
},
"canvas_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{10}$",
"description": "Canvas ID returned by a prior stage=true call to this tool or another openFDA search tool (openfda_search_* or openfda_lookup_ndc). Passing one stages this search onto that canvas (same effect as stage=true) so result sets accumulate for cross-table joins. Omit to stage onto a fresh canvas."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"meta",
"results",
"totalResults"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"meta": {
"type": "object",
"required": [
"total",
"skip",
"limit",
"lastUpdated"
],
"properties": {
"skip": {
"type": "number",
"description": "Pagination offset"
},
"limit": {
"type": "number",
"description": "Page size applied to this request — the requested limit, lowered to the records returned when the 24000-byte inline budget bounded the page (see page_omitted). Not a count of what arrived: a window running past the end of the matched set returns fewer records than this, so read the length of results for the actual count."
},
"total": {
"type": "number",
"description": "Total matching records in the database"
},
"lastUpdated": {
"type": "string",
"description": "Dataset last updated date"
},
"totalUnverified": {
"type": "boolean",
"description": "Present (true) only on an empty page at skip > 0 whose total could not be confirmed: openFDA answers a page past the end and a search that matched nothing alike, and the follow-up request for the total failed. total then reads 0 without ruling out records at a lower skip — re-call with skip=0 to tell."
}
},
"description": "Response metadata",
"additionalProperties": false
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"canvas_disabled",
"canvas_not_found",
"canvas_capacity_exhausted",
"rate_limited",
"upstream_error",
"malformed_search",
"query_error",
"pagination_limit_reached"
],
"description": "Machine-readable failure mode. Declared by this tool: `canvas_disabled`: Staging was requested (stage=true or a canvas_id) but DataCanvas is disabled. `canvas_not_found`: The canvas_id is well-formed but names no active canvas — expired or never minted. `canvas_capacity_exhausted`: canvas_id was omitted and the active canvas cap is already reached. `rate_limited`: The openFDA daily or per-minute request limit is exceeded. `upstream_error`: The openFDA API returned a 5xx server error. `malformed_search`: The search query leaves a double quote, parenthesis, or range bracket unclosed, or ends on a backslash. `query_error`: The search query was rejected by openFDA (malformed field name, invalid syntax). `pagination_limit_reached`: skip exceeds the 25000 record pagination ceiling. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Canvas staging disclosure when the call staged, the fields projection and any requested path no record carries, the byte-budget disclosure when the inline page was bounded or one record exceeds it (with the routes to the withheld records and the largest fields of the oversized record), and guidance when results are empty or paging overshot — how to broaden filters or adjust the query."
},
"results": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"description": "Adverse event records — fields vary by category (drug: patient/reactions/drugs, device: device details/event type, food: products/outcomes)"
},
"spilled": {
"type": "boolean",
"description": "True when this call staged its matched set on the canvas — use canvas_id with openfda_dataframe_describe for its columns, then openfda_dataframe_query for SQL. Absent when staging was not requested."
},
"canvas_id": {
"type": "string",
"description": "DataCanvas session id for the staged result set. Present when this call staged. Pass to openfda_dataframe_query / openfda_dataframe_describe, or back into this tool to accumulate more tables on the same canvas."
},
"truncated": {
"type": "boolean",
"description": "True when fewer rows reached the canvas than matched upstream — staging stopped at its size budget or openFDA's 25000-row pagination ceiling. Narrow the query (filters, date range) for a complete set."
},
"page_bytes": {
"type": "number",
"description": "Serialized size of results in this response, in UTF-8 bytes. Present when the 24000-byte inline budget bounded the page or when the page exceeds it; larger than the budget only when a single record exceeds it on its own, which is returned whole — page_largest_fields then names its largest fields, and fields projects the record to the paths you need."
},
"staged_rows": {
"type": "number",
"description": "Rows written to the canvas table. Compare with meta.total: a smaller value means staging stopped at its size budget and the table holds only the first staged_rows records."
},
"canvas_table": {
"type": "string",
"description": "Canvas table holding the staged rows. Present when rows were staged; list its columns with openfda_dataframe_describe, then reference it in openfda_dataframe_query FROM clauses."
},
"page_omitted": {
"type": "number",
"description": "Records dropped from the requested limit/skip window so the page fit the inline byte budget. Present only when records were dropped. Read them by re-calling with skip advanced by the number of records returned, lower limit for a smaller page (when more than one record came back), pass fields to keep only the paths you need, or pass stage=true to query a bounded drain of the match with openfda_dataframe_query."
},
"totalResults": {
"type": "number",
"description": "Total matching adverse event records in the dataset"
},
"effectiveQuery": {
"type": "string",
"description": "Search filter applied to the query, as submitted to openFDA"
},
"projected_fields": {
"type": "array",
"items": {
"type": "string"
},
"description": "The field paths each record in results was projected to, as requested — present only when the call passed fields. Every record also keeps its identity key; any other field is absent because the projection left it out, not because openFDA lacks it. Re-call without fields for whole records."
},
"page_largest_fields": {
"type": "array",
"items": {
"type": "object",
"required": [
"path",
"bytes"
],
"properties": {
"path": {
"type": "string",
"description": "Dotted field path on the record. Fields of array elements share their array path, so patient.drug.openfda covers that block in every drug of the report."
},
"bytes": {
"type": "number",
"description": "Serialized UTF-8 bytes the field holds, totalled across array elements."
}
},
"description": "One field of the oversized record and its serialized size.",
"additionalProperties": false
},
"description": "Present only when page_bytes exceeds the 24000-byte budget, which happens only when the one record on the page exceeds it on its own: that record's largest fields, largest first — the deepest single field that holds the overrun, or else up to three top-level fields. Leave them out with a fields projection to shrink the record."
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}openfda_search_animal_events — Openfda Search Animal Events
Search adverse event reports for veterinary drugs and devices submitted to the FDA Center for Veterinary Medicine. Records include animal species, breed, age, weight, drug name and route, adverse reactions (using VeDDRA terminology), and outcome. Use to investigate safety signals for veterinary products, find reports by animal species or drug, or explore reaction patterns. Brand and manufacturer names are masked as "MSK" on every record (the FDA null flavor for withheld data), so find a product by active ingredient on drug.active_ingredients.name, unsuffixed. An ingredient search spans every product containing it and cannot attribute reports to one brand; drug.manufacturer.registration_number (the full value, e.g. "USA-USFDACVM-N141426") narrows it to one approval. With stage=true, call openfda_dataframe_describe for the staged columns, then openfda_dataframe_query for SQL.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"skip": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Number of records to skip for pagination (default 0). openFDA caps pagination at 25000 records; a higher value returns a pagination_limit_reached error."
},
"sort": {
"type": "string",
"pattern": "^\\s*(?=\\S)(?![,:.])(?!.*,(?: *[,:.]|\\s*$))(?!.*[^\\s,] +[^\\s,])(?!.*\\.(?:[.:,\\s]|$))(?!.*:(?!(?:[Aa][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?|[Dd][Ee][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?)(?: *,|\\s*$)))[A-Za-z0-9_.:, ]*[A-Za-z0-9_]\\s*$",
"minLength": 1,
"description": "Sort expression — a field path optionally suffixed with :asc or :desc (any letter case; :ascending and :descending are also accepted, and every direction is sent as asc or desc); comma-separate for multi-field sort. Example: original_receive_date:desc. Field paths take only letters, digits, underscores, and dots; any other character or direction is rejected before the request. A well-formed but non-sortable field still causes a query error — use a documented field name."
},
"limit": {
"type": "number",
"default": 10,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1-1000, default 10). Serialized record size varies by three orders of magnitude across openFDA endpoints, so the page is also bounded by a 24000-byte serialized budget: a page that would overrun it returns fewer records than requested and reports the cut on page_omitted. Whenever any record matched, at least one comes back, however large it measures — page_bytes then reports its size and page_largest_fields names its largest fields, which fields can project away."
},
"stage": {
"type": "boolean",
"default": false,
"description": "Stage the matched set on a DataCanvas for SQL analysis — openfda_dataframe_describe lists the staged columns, openfda_dataframe_query runs the SQL. Default false — the call returns one page for one upstream request. When true, records are also drained onto a canvas table up to a size budget (staged_rows reports how many reached it). Staging is for record-level SQL over a bounded slice; for a distribution over everything that matched, openfda_count_values aggregates server-side in one request. Requires CANVAS_PROVIDER_TYPE=duckdb."
},
"fields": {
"type": "array",
"items": {
"type": "string",
"pattern": "^(?!\\.)(?!.*\\.\\.)(?!.*\\.$)[A-Za-z0-9_.]+$"
},
"description": "Dotted record paths to keep on each returned record, e.g. [\"original_receive_date\", \"animal.species\"] — any key path on a returned record works, and openfda_describe_fields lists the searchable ones. Each record is projected to these paths plus its identity key (unique_aer_id_number) before the inline byte budget is measured, identically on both response surfaces, and a path through an array applies to every element. Use it when records are large: a page_bytes over the budget comes with the largest fields of the oversized record. A path no returned record carries is named on the notice. Omit for whole records. Paths take only letters, digits, underscores, and dots; anything else is rejected before the request. With stage=true, only the inline page is projected — the staged table keeps its columns."
},
"search": {
"type": "string",
"pattern": "\\S",
"minLength": 1,
"description": "openFDA search query using field:value syntax. Examples: animal.species:\"Dog\", drug.active_ingredients.name:\"Fluralaner\", reaction.veddra_term_name:\"Vomiting\", serious_ae:\"true\". Omit to browse recent reports. Separate clauses with spaces. A \"+\" written in the URL style of openFDA's examples also reads as a space where it separates: beside AND, OR, NOT, or TO, after a closing quote, paren, or bracket, or between a term and the next \"field:\" prefix (a:\"x\"+AND+b:\"y\", [20200101+TO+20201231], c:x+d:y). In a query that marks a clause required with a leading +field:, only the AND/OR/NOT/TO positions read that way. Any other \"+\" is sent as written, and inside a quoted phrase \"+\" is a literal plus sign. Double quotes, parentheses, and range brackets must balance, and the query must not end on a backslash — each is rejected before the request."
},
"canvas_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{10}$",
"description": "Canvas ID returned by a prior stage=true call to this tool or another openFDA search tool (openfda_search_* or openfda_lookup_ndc). Passing one stages this search onto that canvas (same effect as stage=true) so result sets accumulate for cross-table joins. Omit to stage onto a fresh canvas."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"meta",
"results",
"totalResults"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"meta": {
"type": "object",
"required": [
"total",
"skip",
"limit",
"lastUpdated"
],
"properties": {
"skip": {
"type": "number",
"description": "Pagination offset"
},
"limit": {
"type": "number",
"description": "Page size applied to this request — the requested limit, lowered to the records returned when the 24000-byte inline budget bounded the page (see page_omitted). Not a count of what arrived: a window running past the end of the matched set returns fewer records than this, so read the length of results for the actual count."
},
"total": {
"type": "number",
"description": "Total matching records in the dataset"
},
"lastUpdated": {
"type": "string",
"description": "Dataset last updated date"
},
"totalUnverified": {
"type": "boolean",
"description": "Present (true) only on an empty page at skip > 0 whose total could not be confirmed: openFDA answers a page past the end and a search that matched nothing alike, and the follow-up request for the total failed. total then reads 0 without ruling out records at a lower skip — re-call with skip=0 to tell."
}
},
"description": "Response metadata",
"additionalProperties": false
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"canvas_disabled",
"canvas_not_found",
"canvas_capacity_exhausted",
"rate_limited",
"upstream_error",
"malformed_search",
"query_error",
"pagination_limit_reached"
],
"description": "Machine-readable failure mode. Declared by this tool: `canvas_disabled`: Staging was requested (stage=true or a canvas_id) but DataCanvas is disabled. `canvas_not_found`: The canvas_id is well-formed but names no active canvas — expired or never minted. `canvas_capacity_exhausted`: canvas_id was omitted and the active canvas cap is already reached. `rate_limited`: The openFDA daily or per-minute request limit is exceeded. `upstream_error`: The openFDA API returned a 5xx server error. `malformed_search`: The search query leaves a double quote, parenthesis, or range bracket unclosed, or ends on a backslash. `query_error`: The search query was rejected by openFDA (malformed field name, invalid syntax). `pagination_limit_reached`: skip exceeds the 25000 record pagination ceiling. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Canvas staging disclosure when the call staged, the fields projection and any requested path no record carries, the byte-budget disclosure when the inline page was bounded or one record exceeds it (with the routes to the withheld records and the largest fields of the oversized record), and guidance when results are empty or paging overshot — how to broaden filters or adjust the query."
},
"results": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"description": "Animal adverse event records. Key fields: unique_aer_id_number, original_receive_date, serious_ae, animal (species, gender, breed, age, weight), drug[] (brand_name, active_ingredients, route, dose, administered_by), reaction[] (veddra_term_name, number_of_animals_affected), outcome[] (medical_status), primary_reporter, type_of_information."
},
"spilled": {
"type": "boolean",
"description": "True when this call staged its matched set on the canvas — use canvas_id with openfda_dataframe_describe for its columns, then openfda_dataframe_query for SQL. Absent when staging was not requested."
},
"canvas_id": {
"type": "string",
"description": "DataCanvas session id for the staged result set. Present when this call staged. Pass to openfda_dataframe_query / openfda_dataframe_describe, or back into this tool to accumulate more tables on the same canvas."
},
"truncated": {
"type": "boolean",
"description": "True when fewer rows reached the canvas than matched upstream — staging stopped at its size budget or openFDA's 25000-row pagination ceiling. Narrow the query (filters, date range) for a complete set."
},
"page_bytes": {
"type": "number",
"description": "Serialized size of results in this response, in UTF-8 bytes. Present when the 24000-byte inline budget bounded the page or when the page exceeds it; larger than the budget only when a single record exceeds it on its own, which is returned whole — page_largest_fields then names its largest fields, and fields projects the record to the paths you need."
},
"staged_rows": {
"type": "number",
"description": "Rows written to the canvas table. Compare with meta.total: a smaller value means staging stopped at its size budget and the table holds only the first staged_rows records."
},
"canvas_table": {
"type": "string",
"description": "Canvas table holding the staged rows. Present when rows were staged; list its columns with openfda_dataframe_describe, then reference it in openfda_dataframe_query FROM clauses."
},
"page_omitted": {
"type": "number",
"description": "Records dropped from the requested limit/skip window so the page fit the inline byte budget. Present only when records were dropped. Read them by re-calling with skip advanced by the number of records returned, lower limit for a smaller page (when more than one record came back), pass fields to keep only the paths you need, or pass stage=true to query a bounded drain of the match with openfda_dataframe_query."
},
"totalResults": {
"type": "number",
"description": "Total matching animal adverse event records in the dataset"
},
"effectiveQuery": {
"type": "string",
"description": "Search filter applied to the query, as submitted to openFDA"
},
"projected_fields": {
"type": "array",
"items": {
"type": "string"
},
"description": "The field paths each record in results was projected to, as requested — present only when the call passed fields. Every record also keeps its identity key; any other field is absent because the projection left it out, not because openFDA lacks it. Re-call without fields for whole records."
},
"page_largest_fields": {
"type": "array",
"items": {
"type": "object",
"required": [
"path",
"bytes"
],
"properties": {
"path": {
"type": "string",
"description": "Dotted field path on the record. Fields of array elements share their array path, so patient.drug.openfda covers that block in every drug of the report."
},
"bytes": {
"type": "number",
"description": "Serialized UTF-8 bytes the field holds, totalled across array elements."
}
},
"description": "One field of the oversized record and its serialized size.",
"additionalProperties": false
},
"description": "Present only when page_bytes exceeds the 24000-byte budget, which happens only when the one record on the page exceeds it on its own: that record's largest fields, largest first — the deepest single field that holds the overrun, or else up to three top-level fields. Leave them out with a fields projection to shrink the record."
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}openfda_search_drug_shortages — Openfda Search Drug Shortages
Search FDA drug shortage records. Returns per-product shortage status, availability, therapeutic category, dosage form, manufacturer, and dates. Use to check whether a drug is currently in shortage, find all oncology drugs with supply issues, or retrieve the openfda block (brand_name, product_ndc, rxcui) to chain into openfda_get_drug_label or openfda_lookup_ndc. With stage=true, call openfda_dataframe_describe for the staged columns, then openfda_dataframe_query for SQL.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"skip": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Number of records to skip for pagination (default 0). openFDA caps pagination at 25000 records; a higher value returns a pagination_limit_reached error."
},
"sort": {
"type": "string",
"pattern": "^\\s*(?=\\S)(?![,:.])(?!.*,(?: *[,:.]|\\s*$))(?!.*[^\\s,] +[^\\s,])(?!.*\\.(?:[.:,\\s]|$))(?!.*:(?!(?:[Aa][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?|[Dd][Ee][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?)(?: *,|\\s*$)))[A-Za-z0-9_.:, ]*[A-Za-z0-9_]\\s*$",
"minLength": 1,
"description": "Sort expression — a field path optionally suffixed with :asc or :desc (any letter case; :ascending and :descending are also accepted, and every direction is sent as asc or desc); comma-separate for multi-field sort. Example: update_date:desc. Field paths take only letters, digits, underscores, and dots; any other character or direction is rejected before the request. A well-formed but non-sortable field still causes a query error — use a documented field name."
},
"limit": {
"type": "number",
"default": 10,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1-1000, default 10). Serialized record size varies by three orders of magnitude across openFDA endpoints, so the page is also bounded by a 24000-byte serialized budget: a page that would overrun it returns fewer records than requested and reports the cut on page_omitted. Whenever any record matched, at least one comes back, however large it measures — page_bytes then reports its size and page_largest_fields names its largest fields, which fields can project away."
},
"stage": {
"type": "boolean",
"default": false,
"description": "Stage the matched set on a DataCanvas for SQL analysis — openfda_dataframe_describe lists the staged columns, openfda_dataframe_query runs the SQL. Default false — the call returns one page for one upstream request. When true, records are also drained onto a canvas table up to a size budget (staged_rows reports how many reached it). Staging is for record-level SQL over a bounded slice; for a distribution over everything that matched, openfda_count_values aggregates server-side in one request. Requires CANVAS_PROVIDER_TYPE=duckdb."
},
"fields": {
"type": "array",
"items": {
"type": "string",
"pattern": "^(?!\\.)(?!.*\\.\\.)(?!.*\\.$)[A-Za-z0-9_.]+$"
},
"description": "Dotted record paths to keep on each returned record, e.g. [\"generic_name\", \"status\"] — any key path on a returned record works, and openfda_describe_fields lists the searchable ones. Each record is projected to these paths plus its identity key (package_ndc) before the inline byte budget is measured, identically on both response surfaces, and a path through an array applies to every element. Use it when records are large: a page_bytes over the budget comes with the largest fields of the oversized record. A path no returned record carries is named on the notice. Omit for whole records. Paths take only letters, digits, underscores, and dots; anything else is rejected before the request. With stage=true, only the inline page is projected — the staged table keeps its columns."
},
"search": {
"type": "string",
"pattern": "\\S",
"minLength": 1,
"description": "openFDA search query using field:value syntax. Examples: status:\"Current\", therapeutic_category:\"Oncology\", generic_name:\"carboplatin\", company_name:\"pfizer\". Omit to browse all records. Separate clauses with spaces. A \"+\" written in the URL style of openFDA's examples also reads as a space where it separates: beside AND, OR, NOT, or TO, after a closing quote, paren, or bracket, or between a term and the next \"field:\" prefix (a:\"x\"+AND+b:\"y\", [20200101+TO+20201231], c:x+d:y). In a query that marks a clause required with a leading +field:, only the AND/OR/NOT/TO positions read that way. Any other \"+\" is sent as written, and inside a quoted phrase \"+\" is a literal plus sign. Double quotes, parentheses, and range brackets must balance, and the query must not end on a backslash — each is rejected before the request. Call openfda_describe_fields({ endpoint: \"drug/shortages\" }) for the complete field list."
},
"canvas_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{10}$",
"description": "Canvas ID returned by a prior stage=true call to this tool or another openFDA search tool (openfda_search_* or openfda_lookup_ndc). Passing one stages this search onto that canvas (same effect as stage=true) so result sets accumulate for cross-table joins. Omit to stage onto a fresh canvas."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"meta",
"results",
"totalResults"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"meta": {
"type": "object",
"required": [
"total",
"skip",
"limit",
"lastUpdated"
],
"properties": {
"skip": {
"type": "number",
"description": "Pagination offset"
},
"limit": {
"type": "number",
"description": "Page size applied to this request — the requested limit, lowered to the records returned when the 24000-byte inline budget bounded the page (see page_omitted). Not a count of what arrived: a window running past the end of the matched set returns fewer records than this, so read the length of results for the actual count."
},
"total": {
"type": "number",
"description": "Total matching shortage records in the database"
},
"lastUpdated": {
"type": "string",
"description": "Dataset last updated date"
},
"totalUnverified": {
"type": "boolean",
"description": "Present (true) only on an empty page at skip > 0 whose total could not be confirmed: openFDA answers a page past the end and a search that matched nothing alike, and the follow-up request for the total failed. total then reads 0 without ruling out records at a lower skip — re-call with skip=0 to tell."
}
},
"description": "Response metadata",
"additionalProperties": false
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"canvas_disabled",
"canvas_not_found",
"canvas_capacity_exhausted",
"rate_limited",
"upstream_error",
"malformed_search",
"query_error",
"pagination_limit_reached"
],
"description": "Machine-readable failure mode. Declared by this tool: `canvas_disabled`: Staging was requested (stage=true or a canvas_id) but DataCanvas is disabled. `canvas_not_found`: The canvas_id is well-formed but names no active canvas — expired or never minted. `canvas_capacity_exhausted`: canvas_id was omitted and the active canvas cap is already reached. `rate_limited`: The openFDA daily or per-minute request limit is exceeded. `upstream_error`: The openFDA API returned a 5xx server error. `malformed_search`: The search query leaves a double quote, parenthesis, or range bracket unclosed, or ends on a backslash. `query_error`: The search query was rejected by openFDA (malformed field name, invalid syntax). `pagination_limit_reached`: skip exceeds the 25000 record pagination ceiling. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Canvas staging disclosure when the call staged, the fields projection and any requested path no record carries, the byte-budget disclosure when the inline page was bounded or one record exceeds it (with the routes to the withheld records and the largest fields of the oversized record), and guidance when results are empty — how to broaden filters or correct field names."
},
"results": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"description": "Drug shortage records. Key fields: generic_name, status (\"Current\", \"To Be Discontinued\", or \"Resolved\"), availability, therapeutic_category, dosage_form, presentation, package_ndc, company_name, contact_info, initial_posting_date, update_date, update_type. openfda block contains brand_name, product_ndc, rxcui, spl_set_id for cross-linking."
},
"spilled": {
"type": "boolean",
"description": "True when this call staged its matched set on the canvas — use canvas_id with openfda_dataframe_describe for its columns, then openfda_dataframe_query for SQL. Absent when staging was not requested."
},
"canvas_id": {
"type": "string",
"description": "DataCanvas session id for the staged result set. Present when this call staged. Pass to openfda_dataframe_query / openfda_dataframe_describe, or back into this tool to accumulate more tables on the same canvas."
},
"truncated": {
"type": "boolean",
"description": "True when fewer rows reached the canvas than matched upstream — staging stopped at its size budget or openFDA's 25000-row pagination ceiling. Narrow the query (filters, date range) for a complete set."
},
"page_bytes": {
"type": "number",
"description": "Serialized size of results in this response, in UTF-8 bytes. Present when the 24000-byte inline budget bounded the page or when the page exceeds it; larger than the budget only when a single record exceeds it on its own, which is returned whole — page_largest_fields then names its largest fields, and fields projects the record to the paths you need."
},
"staged_rows": {
"type": "number",
"description": "Rows written to the canvas table. Compare with meta.total: a smaller value means staging stopped at its size budget and the table holds only the first staged_rows records."
},
"canvas_table": {
"type": "string",
"description": "Canvas table holding the staged rows. Present when rows were staged; list its columns with openfda_dataframe_describe, then reference it in openfda_dataframe_query FROM clauses."
},
"page_omitted": {
"type": "number",
"description": "Records dropped from the requested limit/skip window so the page fit the inline byte budget. Present only when records were dropped. Read them by re-calling with skip advanced by the number of records returned, lower limit for a smaller page (when more than one record came back), pass fields to keep only the paths you need, or pass stage=true to query a bounded drain of the match with openfda_dataframe_query."
},
"totalResults": {
"type": "number",
"description": "Total matching drug shortage records in the dataset"
},
"effectiveQuery": {
"type": "string",
"description": "Search filter applied to the drug/shortages query, as submitted to openFDA"
},
"projected_fields": {
"type": "array",
"items": {
"type": "string"
},
"description": "The field paths each record in results was projected to, as requested — present only when the call passed fields. Every record also keeps its identity key; any other field is absent because the projection left it out, not because openFDA lacks it. Re-call without fields for whole records."
},
"page_largest_fields": {
"type": "array",
"items": {
"type": "object",
"required": [
"path",
"bytes"
],
"properties": {
"path": {
"type": "string",
"description": "Dotted field path on the record. Fields of array elements share their array path, so patient.drug.openfda covers that block in every drug of the report."
},
"bytes": {
"type": "number",
"description": "Serialized UTF-8 bytes the field holds, totalled across array elements."
}
},
"description": "One field of the oversized record and its serialized size.",
"additionalProperties": false
},
"description": "Present only when page_bytes exceeds the 24000-byte budget, which happens only when the one record on the page exceeds it on its own: that record's largest fields, largest first — the deepest single field that holds the overrun, or else up to three top-level fields. Leave them out with a fields projection to shrink the record."
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}openfda_search_recalls — Openfda Search Recalls
Search enforcement reports and recall actions across drugs, food, and devices. With stage=true, call openfda_dataframe_describe for the staged columns, then openfda_dataframe_query for SQL.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"category"
],
"properties": {
"skip": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Number of records to skip for pagination (default 0). openFDA caps pagination at 25000 records; a higher value returns a pagination_limit_reached error."
},
"sort": {
"type": "string",
"pattern": "^\\s*(?=\\S)(?![,:.])(?!.*,(?: *[,:.]|\\s*$))(?!.*[^\\s,] +[^\\s,])(?!.*\\.(?:[.:,\\s]|$))(?!.*:(?!(?:[Aa][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?|[Dd][Ee][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?)(?: *,|\\s*$)))[A-Za-z0-9_.:, ]*[A-Za-z0-9_]\\s*$",
"minLength": 1,
"description": "Sort expression — a field path optionally suffixed with :asc or :desc (any letter case; :ascending and :descending are also accepted, and every direction is sent as asc or desc); comma-separate for multi-field sort (e.g. report_date:desc,status.exact:asc). Field paths take only letters, digits, underscores, and dots; any other character or direction is rejected before the request. A well-formed but non-sortable field still causes a query error — use a documented field name."
},
"limit": {
"type": "number",
"default": 10,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1-1000, default 10). Serialized record size varies by three orders of magnitude across openFDA endpoints, so the page is also bounded by a 24000-byte serialized budget: a page that would overrun it returns fewer records than requested and reports the cut on page_omitted. Whenever any record matched, at least one comes back, however large it measures — page_bytes then reports its size and page_largest_fields names its largest fields, which fields can project away. Device records are the largest here — a device enforcement or recall record runs several kilobytes where a drug or food enforcement record is around one."
},
"stage": {
"type": "boolean",
"default": false,
"description": "Stage the matched set on a DataCanvas for SQL analysis — openfda_dataframe_describe lists the staged columns, openfda_dataframe_query runs the SQL. Default false — the call returns one page for one upstream request. When true, records are also drained onto a canvas table up to a size budget (staged_rows reports how many reached it). Staging is for record-level SQL over a bounded slice; for a distribution over everything that matched, openfda_count_values aggregates server-side in one request. Requires CANVAS_PROVIDER_TYPE=duckdb."
},
"fields": {
"type": "array",
"items": {
"type": "string",
"pattern": "^(?!\\.)(?!.*\\.\\.)(?!.*\\.$)[A-Za-z0-9_.]+$"
},
"description": "Dotted record paths to keep on each returned record, e.g. [\"recalling_firm\", \"reason_for_recall\"] — any key path on a returned record works, and openfda_describe_fields lists the searchable ones. Each record is projected to these paths plus its identity key (recall_number, product_res_number) before the inline byte budget is measured, identically on both response surfaces, and a path through an array applies to every element. Use it when records are large: a page_bytes over the budget comes with the largest fields of the oversized record. A path no returned record carries is named on the notice. Omit for whole records. Paths take only letters, digits, underscores, and dots; anything else is rejected before the request. With stage=true, only the inline page is projected — the staged table keeps its columns."
},
"search": {
"type": "string",
"pattern": "\\S",
"minLength": 1,
"description": "openFDA search query. Examples: classification:\"Class I\" (also \"Class II\" or \"Class III\"), recalling_firm:\"pfizer\", reason_for_recall:\"undeclared allergen\". Omit to browse recent. Separate clauses with spaces. A \"+\" written in the URL style of openFDA's examples also reads as a space where it separates: beside AND, OR, NOT, or TO, after a closing quote, paren, or bracket, or between a term and the next \"field:\" prefix (a:\"x\"+AND+b:\"y\", [20200101+TO+20201231], c:x+d:y). In a query that marks a clause required with a leading +field:, only the AND/OR/NOT/TO positions read that way. Any other \"+\" is sent as written, and inside a quoted phrase \"+\" is a literal plus sign. Double quotes, parentheses, and range brackets must balance, and the query must not end on a backslash — each is rejected before the request."
},
"category": {
"enum": [
"drug",
"food",
"device"
],
"type": "string",
"description": "Product category"
},
"endpoint": {
"enum": [
"enforcement",
"recall"
],
"type": "string",
"default": "enforcement",
"description": "Report type. Default enforcement. The recall endpoint is only available for devices."
},
"canvas_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{10}$",
"description": "Canvas ID returned by a prior stage=true call to this tool or another openFDA search tool (openfda_search_* or openfda_lookup_ndc). Passing one stages this search onto that canvas (same effect as stage=true) so result sets accumulate for cross-table joins. Omit to stage onto a fresh canvas."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"meta",
"results",
"totalResults"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"meta": {
"type": "object",
"required": [
"total",
"skip",
"limit",
"lastUpdated"
],
"properties": {
"skip": {
"type": "number",
"description": "Pagination offset"
},
"limit": {
"type": "number",
"description": "Page size applied to this request — the requested limit, lowered to the records returned when the 24000-byte inline budget bounded the page (see page_omitted). Not a count of what arrived: a window running past the end of the matched set returns fewer records than this, so read the length of results for the actual count."
},
"total": {
"type": "number",
"description": "Total matching records"
},
"lastUpdated": {
"type": "string",
"description": "Dataset last updated date"
},
"totalUnverified": {
"type": "boolean",
"description": "Present (true) only on an empty page at skip > 0 whose total could not be confirmed: openFDA answers a page past the end and a search that matched nothing alike, and the follow-up request for the total failed. total then reads 0 without ruling out records at a lower skip — re-call with skip=0 to tell."
}
},
"description": "Response metadata",
"additionalProperties": false
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"recall_endpoint_non_device",
"canvas_disabled",
"canvas_not_found",
"canvas_capacity_exhausted",
"rate_limited",
"upstream_error",
"malformed_search",
"query_error",
"pagination_limit_reached"
],
"description": "Machine-readable failure mode. Declared by this tool: `recall_endpoint_non_device`: The recall endpoint was requested for a non-device category. `canvas_disabled`: Staging was requested (stage=true or a canvas_id) but DataCanvas is disabled. `canvas_not_found`: The canvas_id is well-formed but names no active canvas — expired or never minted. `canvas_capacity_exhausted`: canvas_id was omitted and the active canvas cap is already reached. `rate_limited`: The openFDA daily or per-minute request limit is exceeded. `upstream_error`: The openFDA API returned a 5xx server error. `malformed_search`: The search query leaves a double quote, parenthesis, or range bracket unclosed, or ends on a backslash. `query_error`: The search query was rejected by openFDA (malformed field name, invalid syntax). `pagination_limit_reached`: skip exceeds the 25000 record pagination ceiling. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Canvas staging disclosure when the call staged, the fields projection and any requested path no record carries, the byte-budget disclosure when the inline page was bounded or one record exceeds it (with the routes to the withheld records and the largest fields of the oversized record), and guidance when results are empty — how to broaden filters or correct field names."
},
"results": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"description": "Enforcement or recall records. Enforcement records (every category) carry recall_number, classification, status, voluntary_mandated, recalling_firm, product_description, reason_for_recall, distribution_pattern, report_date. Device recall records name identity product_res_number and status recall_status, add res_event_number and root_cause_description, share the firm, product, reason, and distribution fields, and carry no recall hazard classification."
},
"spilled": {
"type": "boolean",
"description": "True when this call staged its matched set on the canvas — use canvas_id with openfda_dataframe_describe for its columns, then openfda_dataframe_query for SQL. Absent when staging was not requested."
},
"canvas_id": {
"type": "string",
"description": "DataCanvas session id for the staged result set. Present when this call staged. Pass to openfda_dataframe_query / openfda_dataframe_describe, or back into this tool to accumulate more tables on the same canvas."
},
"truncated": {
"type": "boolean",
"description": "True when fewer rows reached the canvas than matched upstream — staging stopped at its size budget or openFDA's 25000-row pagination ceiling. Narrow the query (filters, date range) for a complete set."
},
"page_bytes": {
"type": "number",
"description": "Serialized size of results in this response, in UTF-8 bytes. Present when the 24000-byte inline budget bounded the page or when the page exceeds it; larger than the budget only when a single record exceeds it on its own, which is returned whole — page_largest_fields then names its largest fields, and fields projects the record to the paths you need."
},
"staged_rows": {
"type": "number",
"description": "Rows written to the canvas table. Compare with meta.total: a smaller value means staging stopped at its size budget and the table holds only the first staged_rows records."
},
"canvas_table": {
"type": "string",
"description": "Canvas table holding the staged rows. Present when rows were staged; list its columns with openfda_dataframe_describe, then reference it in openfda_dataframe_query FROM clauses."
},
"page_omitted": {
"type": "number",
"description": "Records dropped from the requested limit/skip window so the page fit the inline byte budget. Present only when records were dropped. Read them by re-calling with skip advanced by the number of records returned, lower limit for a smaller page (when more than one record came back), pass fields to keep only the paths you need, or pass stage=true to query a bounded drain of the match with openfda_dataframe_query."
},
"totalResults": {
"type": "number",
"description": "Total matching recall/enforcement records in the dataset"
},
"effectiveQuery": {
"type": "string",
"description": "Search filter applied to the recall query, as submitted to openFDA"
},
"projected_fields": {
"type": "array",
"items": {
"type": "string"
},
"description": "The field paths each record in results was projected to, as requested — present only when the call passed fields. Every record also keeps its identity key; any other field is absent because the projection left it out, not because openFDA lacks it. Re-call without fields for whole records."
},
"page_largest_fields": {
"type": "array",
"items": {
"type": "object",
"required": [
"path",
"bytes"
],
"properties": {
"path": {
"type": "string",
"description": "Dotted field path on the record. Fields of array elements share their array path, so patient.drug.openfda covers that block in every drug of the report."
},
"bytes": {
"type": "number",
"description": "Serialized UTF-8 bytes the field holds, totalled across array elements."
}
},
"description": "One field of the oversized record and its serialized size.",
"additionalProperties": false
},
"description": "Present only when page_bytes exceeds the 24000-byte budget, which happens only when the one record on the page exceeds it on its own: that record's largest fields, largest first — the deepest single field that holds the overrun, or else up to three top-level fields. Leave them out with a fields projection to shrink the record."
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}openfda_search_tobacco_reports — Openfda Search Tobacco Reports
Search problem reports submitted to the FDA for tobacco products, including e-cigarettes, vaping products, cigarettes, and smokeless tobacco. Reports capture product type, reported health problems (e.g. seizure, chest pain), product problems (e.g. exploding battery), whether a non-user was affected, and submission date. Use to investigate safety signals, find reports by product type, or analyze health effects. With stage=true, call openfda_dataframe_describe for the staged columns, then openfda_dataframe_query for SQL.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"skip": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Number of records to skip for pagination (default 0). openFDA caps pagination at 25000 records; a higher value returns a pagination_limit_reached error."
},
"sort": {
"type": "string",
"pattern": "^\\s*(?=\\S)(?![,:.])(?!.*,(?: *[,:.]|\\s*$))(?!.*[^\\s,] +[^\\s,])(?!.*\\.(?:[.:,\\s]|$))(?!.*:(?!(?:[Aa][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?|[Dd][Ee][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?)(?: *,|\\s*$)))[A-Za-z0-9_.:, ]*[A-Za-z0-9_]\\s*$",
"minLength": 1,
"description": "Sort expression — a field path optionally suffixed with :asc or :desc (any letter case; :ascending and :descending are also accepted, and every direction is sent as asc or desc); comma-separate for multi-field sort. Example: date_submitted:desc. Field paths take only letters, digits, underscores, and dots; any other character or direction is rejected before the request. A well-formed but non-sortable field still causes a query error — use a documented field name."
},
"limit": {
"type": "number",
"default": 10,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1-1000, default 10). Serialized record size varies by three orders of magnitude across openFDA endpoints, so the page is also bounded by a 24000-byte serialized budget: a page that would overrun it returns fewer records than requested and reports the cut on page_omitted. Whenever any record matched, at least one comes back, however large it measures — page_bytes then reports its size and page_largest_fields names its largest fields, which fields can project away."
},
"stage": {
"type": "boolean",
"default": false,
"description": "Stage the matched set on a DataCanvas for SQL analysis — openfda_dataframe_describe lists the staged columns, openfda_dataframe_query runs the SQL. Default false — the call returns one page for one upstream request. When true, records are also drained onto a canvas table up to a size budget (staged_rows reports how many reached it). Staging is for record-level SQL over a bounded slice; for a distribution over everything that matched, openfda_count_values aggregates server-side in one request. Requires CANVAS_PROVIDER_TYPE=duckdb."
},
"fields": {
"type": "array",
"items": {
"type": "string",
"pattern": "^(?!\\.)(?!.*\\.\\.)(?!.*\\.$)[A-Za-z0-9_.]+$"
},
"description": "Dotted record paths to keep on each returned record, e.g. [\"date_submitted\", \"reported_health_problems\"] — any key path on a returned record works, and openfda_describe_fields lists the searchable ones. Each record is projected to these paths plus its identity key (report_id) before the inline byte budget is measured, identically on both response surfaces, and a path through an array applies to every element. Use it when records are large: a page_bytes over the budget comes with the largest fields of the oversized record. A path no returned record carries is named on the notice. Omit for whole records. Paths take only letters, digits, underscores, and dots; anything else is rejected before the request. With stage=true, only the inline page is projected — the staged table keeps its columns."
},
"search": {
"type": "string",
"pattern": "\\S",
"minLength": 1,
"description": "openFDA search query using field:value syntax. Examples: tobacco_products:\"Electronic cigarette\", reported_health_problems:\"Seizure\", nonuser_affected:\"Yes\". Omit to browse recent reports. Separate clauses with spaces. A \"+\" written in the URL style of openFDA's examples also reads as a space where it separates: beside AND, OR, NOT, or TO, after a closing quote, paren, or bracket, or between a term and the next \"field:\" prefix (a:\"x\"+AND+b:\"y\", [20200101+TO+20201231], c:x+d:y). In a query that marks a clause required with a leading +field:, only the AND/OR/NOT/TO positions read that way. Any other \"+\" is sent as written, and inside a quoted phrase \"+\" is a literal plus sign. Double quotes, parentheses, and range brackets must balance, and the query must not end on a backslash — each is rejected before the request."
},
"canvas_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{10}$",
"description": "Canvas ID returned by a prior stage=true call to this tool or another openFDA search tool (openfda_search_* or openfda_lookup_ndc). Passing one stages this search onto that canvas (same effect as stage=true) so result sets accumulate for cross-table joins. Omit to stage onto a fresh canvas."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"meta",
"results",
"totalResults"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"meta": {
"type": "object",
"required": [
"total",
"skip",
"limit",
"lastUpdated"
],
"properties": {
"skip": {
"type": "number",
"description": "Pagination offset"
},
"limit": {
"type": "number",
"description": "Page size applied to this request — the requested limit, lowered to the records returned when the 24000-byte inline budget bounded the page (see page_omitted). Not a count of what arrived: a window running past the end of the matched set returns fewer records than this, so read the length of results for the actual count."
},
"total": {
"type": "number",
"description": "Total matching records in the dataset"
},
"lastUpdated": {
"type": "string",
"description": "Dataset last updated date"
},
"totalUnverified": {
"type": "boolean",
"description": "Present (true) only on an empty page at skip > 0 whose total could not be confirmed: openFDA answers a page past the end and a search that matched nothing alike, and the follow-up request for the total failed. total then reads 0 without ruling out records at a lower skip — re-call with skip=0 to tell."
}
},
"description": "Response metadata",
"additionalProperties": false
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"canvas_disabled",
"canvas_not_found",
"canvas_capacity_exhausted",
"rate_limited",
"upstream_error",
"malformed_search",
"query_error",
"pagination_limit_reached"
],
"description": "Machine-readable failure mode. Declared by this tool: `canvas_disabled`: Staging was requested (stage=true or a canvas_id) but DataCanvas is disabled. `canvas_not_found`: The canvas_id is well-formed but names no active canvas — expired or never minted. `canvas_capacity_exhausted`: canvas_id was omitted and the active canvas cap is already reached. `rate_limited`: The openFDA daily or per-minute request limit is exceeded. `upstream_error`: The openFDA API returned a 5xx server error. `malformed_search`: The search query leaves a double quote, parenthesis, or range bracket unclosed, or ends on a backslash. `query_error`: The search query was rejected by openFDA (malformed field name, invalid syntax). `pagination_limit_reached`: skip exceeds the 25000 record pagination ceiling. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Canvas staging disclosure when the call staged, the fields projection and any requested path no record carries, the byte-budget disclosure when the inline page was bounded or one record exceeds it (with the routes to the withheld records and the largest fields of the oversized record), and guidance when results are empty or paging overshot — how to broaden filters or adjust the query."
},
"results": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"description": "Tobacco problem report records. Key fields: report_id, date_submitted, tobacco_products[] (product type description), reported_health_problems[] (health effects), reported_product_problems[] (device/product defects), number_tobacco_products, number_health_problems, number_product_problems, nonuser_affected."
},
"spilled": {
"type": "boolean",
"description": "True when this call staged its matched set on the canvas — use canvas_id with openfda_dataframe_describe for its columns, then openfda_dataframe_query for SQL. Absent when staging was not requested."
},
"canvas_id": {
"type": "string",
"description": "DataCanvas session id for the staged result set. Present when this call staged. Pass to openfda_dataframe_query / openfda_dataframe_describe, or back into this tool to accumulate more tables on the same canvas."
},
"truncated": {
"type": "boolean",
"description": "True when fewer rows reached the canvas than matched upstream — staging stopped at its size budget or openFDA's 25000-row pagination ceiling. Narrow the query (filters, date range) for a complete set."
},
"page_bytes": {
"type": "number",
"description": "Serialized size of results in this response, in UTF-8 bytes. Present when the 24000-byte inline budget bounded the page or when the page exceeds it; larger than the budget only when a single record exceeds it on its own, which is returned whole — page_largest_fields then names its largest fields, and fields projects the record to the paths you need."
},
"staged_rows": {
"type": "number",
"description": "Rows written to the canvas table. Compare with meta.total: a smaller value means staging stopped at its size budget and the table holds only the first staged_rows records."
},
"canvas_table": {
"type": "string",
"description": "Canvas table holding the staged rows. Present when rows were staged; list its columns with openfda_dataframe_describe, then reference it in openfda_dataframe_query FROM clauses."
},
"page_omitted": {
"type": "number",
"description": "Records dropped from the requested limit/skip window so the page fit the inline byte budget. Present only when records were dropped. Read them by re-calling with skip advanced by the number of records returned, lower limit for a smaller page (when more than one record came back), pass fields to keep only the paths you need, or pass stage=true to query a bounded drain of the match with openfda_dataframe_query."
},
"totalResults": {
"type": "number",
"description": "Total matching tobacco problem reports in the dataset"
},
"effectiveQuery": {
"type": "string",
"description": "Search filter applied to the query, as submitted to openFDA"
},
"projected_fields": {
"type": "array",
"items": {
"type": "string"
},
"description": "The field paths each record in results was projected to, as requested — present only when the call passed fields. Every record also keeps its identity key; any other field is absent because the projection left it out, not because openFDA lacks it. Re-call without fields for whole records."
},
"page_largest_fields": {
"type": "array",
"items": {
"type": "object",
"required": [
"path",
"bytes"
],
"properties": {
"path": {
"type": "string",
"description": "Dotted field path on the record. Fields of array elements share their array path, so patient.drug.openfda covers that block in every drug of the report."
},
"bytes": {
"type": "number",
"description": "Serialized UTF-8 bytes the field holds, totalled across array elements."
}
},
"description": "One field of the oversized record and its serialized size.",
"additionalProperties": false
},
"description": "Present only when page_bytes exceeds the 24000-byte budget, which happens only when the one record on the page exceeds it on its own: that record's largest fields, largest first — the deepest single field that holds the overrun, or else up to three top-level fields. Leave them out with a fields projection to shrink the record."
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}openfda_count_values — Openfda Count Values
Aggregate and tally unique values for any field across any openFDA endpoint. Returns ranked term-count pairs sorted by count descending; a date field tallies one term per distinct YYYYMMDD date, ranked the same way. Pair with openfda_search_adverse_events, openfda_search_drug_approvals, openfda_search_device_clearances, openfda_search_recalls, openfda_get_drug_label, or openfda_lookup_ndc when sample records help interpret the aggregates.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"endpoint",
"count"
],
"properties": {
"count": {
"type": "string",
"pattern": "\\S",
"minLength": 1,
"description": "Field to count. openfda_describe_fields gives the verified expression per field as countAs (null = not countable in any form). Otherwise: append .exact for whole-phrase counting of free-text fields (e.g. \"patient.reaction.reactionmeddrapt.exact\"); count identifier fields openFDA already indexes as keywords (product_ndc, application_number, pma_number) bare — .exact on those is rejected as not countable. A date field (receivedate, report_date, decision_date) is counted bare and yields one term per distinct YYYYMMDD date."
},
"limit": {
"type": "number",
"default": 100,
"maximum": 1000,
"minimum": 1,
"description": "Number of top terms to return (default 100, max 1000 — openFDA's own count maximum). truncated reports whether more distinct terms exist beyond it, except at the maximum itself, where openFDA offers no way to tell. A date field is the exception: openFDA sends every date, so the tool ranks them all and truncated is exact at any limit; scope search to at most limit dates to list each one."
},
"search": {
"type": "string",
"pattern": "\\S",
"minLength": 1,
"description": "Filter query to scope the count (e.g. patient.drug.medicinalproduct:\"metformin\"). Omit to count across every record in the endpoint. Separate clauses with spaces. A \"+\" written in the URL style of openFDA's examples also reads as a space where it separates: beside AND, OR, NOT, or TO, after a closing quote, paren, or bracket, or between a term and the next \"field:\" prefix (a:\"x\"+AND+b:\"y\", [20200101+TO+20201231], c:x+d:y). In a query that marks a clause required with a leading +field:, only the AND/OR/NOT/TO positions read that way. Any other \"+\" is sent as written, and inside a quoted phrase \"+\" is a literal plus sign. Double quotes, parentheses, and range brackets must balance, and the query must not end on a backslash — each is rejected before the request."
},
"endpoint": {
"enum": [
"drug/event",
"drug/label",
"drug/enforcement",
"drug/ndc",
"drug/drugsfda",
"drug/shortages",
"food/event",
"food/enforcement",
"device/event",
"device/510k",
"device/pma",
"device/recall",
"device/enforcement",
"device/classification",
"device/registrationlisting",
"device/udi",
"device/covid19serology",
"animalandveterinary/event",
"tobacco/problem",
"other/substance"
],
"type": "string",
"description": "Full openFDA endpoint path (e.g. \"drug/event\", \"device/classification\")"
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"meta",
"results",
"termCount"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"cap": {
"type": "number",
"description": "The limit applied to the term list."
},
"meta": {
"type": "object",
"required": [
"lastUpdated"
],
"properties": {
"lastUpdated": {
"type": "string",
"description": "Dataset last updated date"
}
},
"description": "Response metadata",
"additionalProperties": false
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"rate_limited",
"upstream_error",
"malformed_search",
"query_error",
"not_aggregatable"
],
"description": "Machine-readable failure mode. Declared by this tool: `rate_limited`: The openFDA daily or per-minute request limit is exceeded. `upstream_error`: The openFDA API returned a 5xx server error. `malformed_search`: The search query leaves a double quote, parenthesis, or range bracket unclosed, or ends on a backslash. `query_error`: The search or count query was rejected by openFDA (malformed field name, invalid syntax). `not_aggregatable`: openFDA cannot aggregate the count expression as written — an analyzed text field, .exact on a field already indexed as a keyword, or a field with no countable form. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"shown": {
"type": "number",
"description": "Number of terms returned in this response."
},
"notice": {
"type": "string",
"description": "Why the tally is empty — the search matched no records, or the matched records carry no value for the field — and how to widen it; when truncated, how to reach the omitted terms (on a date field, how many distinct dates were counted); at the 1000-term maximum, that openFDA cannot show whether more distinct values exist; how many count rows openFDA sent without a value or numeric count, which are left out. Absent when a complete list is returned."
},
"results": {
"type": "array",
"items": {
"type": "object",
"required": [
"term",
"count"
],
"properties": {
"term": {
"type": "string",
"description": "Field value; on a date field, the date as YYYYMMDD"
},
"count": {
"type": "number",
"description": "Number of occurrences"
}
},
"description": "A single term-count pair",
"additionalProperties": false
},
"description": "Term-count pairs sorted by count descending"
},
"termCount": {
"type": "number",
"description": "Number of distinct terms returned"
},
"truncated": {
"type": "boolean",
"description": "True when at least one more distinct term exists beyond the limit. Absent when the list is complete, and at the 1000-term maximum, where openFDA cannot show whether more exist (notice says so). Exact at any limit on a date field, where every date arrives."
},
"truncationCeiling": {
"type": "number",
"description": "Count of the lowest-ranked term returned — omitted terms fall at or below it."
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}openfda_describe_fields — Openfda Describe Fields
Return the searchable field paths for an openFDA endpoint, grouped by category with type and description. Use before constructing a search query to find the correct dotted field path — field names differ per endpoint and are not discoverable from the tool schema alone.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"endpoint"
],
"properties": {
"endpoint": {
"enum": [
"drug/event",
"drug/label",
"drug/enforcement",
"drug/ndc",
"drug/drugsfda",
"drug/shortages",
"food/event",
"food/enforcement",
"device/event",
"device/510k",
"device/pma",
"device/recall",
"device/enforcement",
"device/classification",
"device/registrationlisting",
"device/udi",
"device/covid19serology",
"animalandveterinary/event",
"tobacco/problem",
"other/substance"
],
"type": "string",
"description": "openFDA endpoint to describe (e.g. \"drug/event\", \"drug/shortages\", \"device/510k\"). Must be one of the cataloged endpoints."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"endpoint",
"groups",
"queryTips"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "Machine-readable failure mode."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"groups": {
"type": "array",
"items": {
"type": "object",
"required": [
"label",
"fields"
],
"properties": {
"label": {
"type": "string",
"description": "Field group label"
},
"fields": {
"type": "array",
"items": {
"type": "object",
"required": [
"path",
"type",
"countAs",
"note"
],
"properties": {
"note": {
"type": "string",
"description": "What this field contains"
},
"path": {
"type": "string",
"description": "Dotted field path for use in search queries"
},
"type": {
"type": "string",
"description": "Data type (string, date, integer, float, boolean)"
},
"countAs": {
"type": [
"string",
"null"
],
"description": "Verified expression to pass as count in openfda_count_values (the bare path or path.exact); null when openFDA cannot aggregate the field in any form"
}
},
"description": "A single searchable field entry",
"additionalProperties": false
},
"description": "Searchable fields in this group"
}
},
"description": "A named group of related fields",
"additionalProperties": false
},
"description": "Field groups for this endpoint"
},
"endpoint": {
"type": "string",
"description": "The endpoint these fields apply to"
},
"queryTips": {
"type": "string",
"description": "openFDA query syntax reminders relevant to this endpoint"
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}openfda_get_drug_label — Openfda Get Drug Label
Look up FDA drug labeling (package inserts / SPL documents). Check indications, warnings, dosage, contraindications, active ingredients, or any structured label section. A label runs to tens of thousands of tokens, so a page that exceeds the inline budget returns the list of available sections instead; re-call with sections to pull the ones you need.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"search"
],
"properties": {
"skip": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Number of records to skip for pagination (default 0). openFDA caps pagination at 25000 records; a higher value returns a pagination_limit_reached error."
},
"sort": {
"type": "string",
"pattern": "^\\s*(?=\\S)(?![,:.])(?!.*,(?: *[,:.]|\\s*$))(?!.*[^\\s,] +[^\\s,])(?!.*\\.(?:[.:,\\s]|$))(?!.*:(?!(?:[Aa][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?|[Dd][Ee][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?)(?: *,|\\s*$)))[A-Za-z0-9_.:, ]*[A-Za-z0-9_]\\s*$",
"minLength": 1,
"description": "Sort expression — a field path optionally suffixed with :asc or :desc (any letter case; :ascending and :descending are also accepted, and every direction is sent as asc or desc); comma-separate for multi-field sort. Example: effective_time:desc. Field paths take only letters, digits, underscores, and dots; any other character or direction is rejected before the request. A well-formed but non-sortable field still causes a query error — use a documented field name."
},
"limit": {
"type": "number",
"default": 5,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of results to return (1-1000). Default 5. Labels are large, and the cost of a sections selection is the section summed across every record on the page — so it scales with this limit. Lower it before widening a selection."
},
"search": {
"type": "string",
"pattern": "\\S",
"minLength": 1,
"description": "Query targeting label fields. Examples: openfda.brand_name:\"aspirin\", openfda.generic_name:\"metformin\", openfda.manufacturer_name:\"pfizer\". To fetch one product's label, pass set_id with the SPL UUID returned in earlier results; it returns the label's current revision only, since openFDA does not serve earlier ones. Separate clauses with spaces. A \"+\" written in the URL style of openFDA's examples also reads as a space where it separates: beside AND, OR, NOT, or TO, after a closing quote, paren, or bracket, or between a term and the next \"field:\" prefix (a:\"x\"+AND+b:\"y\", [20200101+TO+20201231], c:x+d:y). In a query that marks a clause required with a leading +field:, only the AND/OR/NOT/TO positions read that way. Any other \"+\" is sent as written, and inside a quoted phrase \"+\" is a literal plus sign. Double quotes, parentheses, and range brackets must balance, and the query must not end on a backslash — each is rejected before the request."
},
"sections": {
"type": "array",
"items": {
"type": "string",
"pattern": "\\S",
"minLength": 1,
"description": "A top-level label section name."
},
"description": "Label sections to return, e.g. [\"boxed_warning\",\"indications_and_usage\"]. Names come from the outline an oversized page returns, or from openfda_describe_fields. Omit for the whole label — which returns the section outline instead when the page exceeds the inline size budget; an empty list is treated as omitted. A selection is returned whole even when it exceeds that budget, with its serialized size reported on the notice; the outline names a section measured to fit at the requested limit. Sections ending in _table hold SPL table markup: raw in structured results, rendered as Markdown tables in the text output. Metadata (openfda, set_id, id, effective_time, version) is returned either way and counts toward the size."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"meta",
"kind",
"totalResults",
"effectiveQuery"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"cap": {
"type": "number",
"description": "The limit applied to this page."
},
"kind": {
"enum": [
"full",
"outline"
],
"type": "string",
"description": "Whether this response carries label records (\"full\") or only the section outline of a page too large to inline (\"outline\")."
},
"meta": {
"type": "object",
"required": [
"total",
"skip",
"limit",
"lastUpdated"
],
"properties": {
"skip": {
"type": "number",
"description": "Number of results skipped."
},
"limit": {
"type": "number",
"description": "Maximum results returned per request."
},
"total": {
"type": "number",
"description": "Total matching results in the dataset."
},
"lastUpdated": {
"type": "string",
"description": "Date the dataset was last updated."
},
"totalUnverified": {
"type": "boolean",
"description": "Present (true) only on an empty page at skip > 0 whose total could not be confirmed: openFDA answers a page past the end and a search that matched nothing alike, and the follow-up request for the total failed. total then reads 0 without ruling out records at a lower skip — re-call with skip=0 to tell."
}
},
"description": "Pagination and freshness metadata.",
"additionalProperties": false
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"rate_limited",
"upstream_error",
"malformed_search",
"query_error",
"pagination_limit_reached"
],
"description": "Machine-readable failure mode. Declared by this tool: `rate_limited`: The openFDA daily or per-minute request limit is exceeded. `upstream_error`: The openFDA API returned a 5xx server error. `malformed_search`: The search query leaves a double quote, parenthesis, or range bracket unclosed, or ends on a backslash. `query_error`: The search query was rejected by openFDA (malformed field name, invalid syntax). `pagination_limit_reached`: skip exceeds the 25000 record pagination ceiling. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"shown": {
"type": "number",
"description": "Number of labels returned in this response."
},
"notice": {
"type": "string",
"description": "Guidance for this page: how to broaden filters or correct field names when results are empty or paging overshot, the sized re-call example when a page overflowed to its section outline, section names no record carried, and the serialized size when a sections selection exceeds the inline budget. Absent when nothing needs saying."
},
"outline": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"bytes"
],
"properties": {
"name": {
"type": "string",
"description": "Section identifier — pass in `sections` to retrieve it"
},
"bytes": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0,
"description": "Serialized byte size of the section"
}
},
"description": "One retrievable label section and its serialized size.",
"additionalProperties": false
},
"description": "Section names available across the matched page and their serialized size, largest first. Present when kind is \"outline\" — pass names back in sections to retrieve them."
},
"results": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"description": "Drug label records, present when kind is \"full\". Each carries an openfda block (brand_name, generic_name, manufacturer_name, route) plus optional SPL sections like indications_and_usage, warnings, dosage_and_administration, contraindications, adverse_reactions; section presence varies per label. Sections ending in _table carry raw SPL table markup (tags and character entities) exactly as openFDA returns it. Narrowed to the requested sections plus metadata when sections was supplied."
},
"truncated": {
"type": "boolean",
"description": "True when more labels matched than this page returned — page with skip for the rest."
},
"totalResults": {
"type": "number",
"description": "Total matching label records in the dataset"
},
"effectiveQuery": {
"type": "string",
"description": "Search filter applied to the drug label query, as submitted to openFDA"
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}openfda_search_drug_approvals — Openfda Search Drug Approvals
Search the Drugs@FDA database for drug application approvals (NDAs and ANDAs). Returns application details, sponsor info, and full submission history. Pair with openfda_get_drug_label to read the approved label, or openfda_count_values to aggregate by sponsor_name, product_type, or route. With stage=true, call openfda_dataframe_describe for the staged columns, then openfda_dataframe_query for SQL.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"skip": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Number of records to skip for pagination (default 0). openFDA caps pagination at 25000 records; a higher value returns a pagination_limit_reached error."
},
"sort": {
"type": "string",
"pattern": "^\\s*(?=\\S)(?![,:.])(?!.*,(?: *[,:.]|\\s*$))(?!.*[^\\s,] +[^\\s,])(?!.*\\.(?:[.:,\\s]|$))(?!.*:(?!(?:[Aa][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?|[Dd][Ee][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?)(?: *,|\\s*$)))[A-Za-z0-9_.:, ]*[A-Za-z0-9_]\\s*$",
"minLength": 1,
"description": "Sort expression — a field path optionally suffixed with :asc or :desc (any letter case; :ascending and :descending are also accepted, and every direction is sent as asc or desc); comma-separate for multi-field sort. Example: submissions.submission_status_date:desc. Field paths take only letters, digits, underscores, and dots; any other character or direction is rejected before the request. A well-formed but non-sortable field still causes a query error — use a documented field name."
},
"limit": {
"type": "number",
"default": 10,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1-1000, default 10). Serialized record size varies by three orders of magnitude across openFDA endpoints, so the page is also bounded by a 24000-byte serialized budget: a page that would overrun it returns fewer records than requested and reports the cut on page_omitted. Whenever any record matched, at least one comes back, however large it measures — page_bytes then reports its size and page_largest_fields names its largest fields, which fields can project away. A record carries its application's whole submission history, so a long-running application is an order of magnitude larger than a recent one."
},
"stage": {
"type": "boolean",
"default": false,
"description": "Stage the matched set on a DataCanvas for SQL analysis — openfda_dataframe_describe lists the staged columns, openfda_dataframe_query runs the SQL. Default false — the call returns one page for one upstream request. When true, records are also drained onto a canvas table up to a size budget (staged_rows reports how many reached it). Staging is for record-level SQL over a bounded slice; for a distribution over everything that matched, openfda_count_values aggregates server-side in one request. Requires CANVAS_PROVIDER_TYPE=duckdb."
},
"fields": {
"type": "array",
"items": {
"type": "string",
"pattern": "^(?!\\.)(?!.*\\.\\.)(?!.*\\.$)[A-Za-z0-9_.]+$"
},
"description": "Dotted record paths to keep on each returned record, e.g. [\"sponsor_name\", \"submissions.submission_status_date\"] — any key path on a returned record works, and openfda_describe_fields lists the searchable ones. Each record is projected to these paths plus its identity key (application_number) before the inline byte budget is measured, identically on both response surfaces, and a path through an array applies to every element. Use it when records are large: a page_bytes over the budget comes with the largest fields of the oversized record. A path no returned record carries is named on the notice. Omit for whole records. Paths take only letters, digits, underscores, and dots; anything else is rejected before the request. With stage=true, only the inline page is projected — the staged table keeps its columns."
},
"search": {
"type": "string",
"pattern": "\\S",
"minLength": 1,
"description": "openFDA search query. Examples: openfda.brand_name:\"humira\", sponsor_name:\"PFIZER\", submissions.submission_type:\"ORIG\" AND submissions.review_priority:\"PRIORITY\". Exact quoted values can be case-sensitive on some fields — sponsor_name is stored uppercase, so use sponsor_name:\"PFIZER\" (a lowercase quoted value returns no matches). Omit to browse recent. Separate clauses with spaces. A \"+\" written in the URL style of openFDA's examples also reads as a space where it separates: beside AND, OR, NOT, or TO, after a closing quote, paren, or bracket, or between a term and the next \"field:\" prefix (a:\"x\"+AND+b:\"y\", [20200101+TO+20201231], c:x+d:y). In a query that marks a clause required with a leading +field:, only the AND/OR/NOT/TO positions read that way. Any other \"+\" is sent as written, and inside a quoted phrase \"+\" is a literal plus sign. Double quotes, parentheses, and range brackets must balance, and the query must not end on a backslash — each is rejected before the request."
},
"canvas_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{10}$",
"description": "Canvas ID returned by a prior stage=true call to this tool or another openFDA search tool (openfda_search_* or openfda_lookup_ndc). Passing one stages this search onto that canvas (same effect as stage=true) so result sets accumulate for cross-table joins. Omit to stage onto a fresh canvas."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"meta",
"results",
"totalResults"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"meta": {
"type": "object",
"required": [
"total",
"skip",
"limit",
"lastUpdated"
],
"properties": {
"skip": {
"type": "number",
"description": "Pagination offset"
},
"limit": {
"type": "number",
"description": "Page size applied to this request — the requested limit, lowered to the records returned when the 24000-byte inline budget bounded the page (see page_omitted). Not a count of what arrived: a window running past the end of the matched set returns fewer records than this, so read the length of results for the actual count."
},
"total": {
"type": "number",
"description": "Total matching records"
},
"lastUpdated": {
"type": "string",
"description": "Dataset last updated date"
},
"totalUnverified": {
"type": "boolean",
"description": "Present (true) only on an empty page at skip > 0 whose total could not be confirmed: openFDA answers a page past the end and a search that matched nothing alike, and the follow-up request for the total failed. total then reads 0 without ruling out records at a lower skip — re-call with skip=0 to tell."
}
},
"description": "Response metadata",
"additionalProperties": false
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"canvas_disabled",
"canvas_not_found",
"canvas_capacity_exhausted",
"rate_limited",
"upstream_error",
"malformed_search",
"query_error",
"pagination_limit_reached"
],
"description": "Machine-readable failure mode. Declared by this tool: `canvas_disabled`: Staging was requested (stage=true or a canvas_id) but DataCanvas is disabled. `canvas_not_found`: The canvas_id is well-formed but names no active canvas — expired or never minted. `canvas_capacity_exhausted`: canvas_id was omitted and the active canvas cap is already reached. `rate_limited`: The openFDA daily or per-minute request limit is exceeded. `upstream_error`: The openFDA API returned a 5xx server error. `malformed_search`: The search query leaves a double quote, parenthesis, or range bracket unclosed, or ends on a backslash. `query_error`: The search query was rejected by openFDA (malformed field name, invalid syntax). `pagination_limit_reached`: skip exceeds the 25000 record pagination ceiling. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Canvas staging disclosure when the call staged, the fields projection and any requested path no record carries, the byte-budget disclosure when the inline page was bounded or one record exceeds it (with the routes to the withheld records and the largest fields of the oversized record), and guidance when results are empty — how to broaden filters or correct field names."
},
"results": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"description": "Drug application records — application_number, sponsor_name, openfda block (brand_name, generic_name, route, product_type, substance_name), products[] (active_ingredients, dosage_form, marketing_status), submissions[] (submission_type, submission_status, submission_status_date, review_priority)."
},
"spilled": {
"type": "boolean",
"description": "True when this call staged its matched set on the canvas — use canvas_id with openfda_dataframe_describe for its columns, then openfda_dataframe_query for SQL. Absent when staging was not requested."
},
"canvas_id": {
"type": "string",
"description": "DataCanvas session id for the staged result set. Present when this call staged. Pass to openfda_dataframe_query / openfda_dataframe_describe, or back into this tool to accumulate more tables on the same canvas."
},
"truncated": {
"type": "boolean",
"description": "True when fewer rows reached the canvas than matched upstream — staging stopped at its size budget or openFDA's 25000-row pagination ceiling. Narrow the query (filters, date range) for a complete set."
},
"page_bytes": {
"type": "number",
"description": "Serialized size of results in this response, in UTF-8 bytes. Present when the 24000-byte inline budget bounded the page or when the page exceeds it; larger than the budget only when a single record exceeds it on its own, which is returned whole — page_largest_fields then names its largest fields, and fields projects the record to the paths you need."
},
"staged_rows": {
"type": "number",
"description": "Rows written to the canvas table. Compare with meta.total: a smaller value means staging stopped at its size budget and the table holds only the first staged_rows records."
},
"canvas_table": {
"type": "string",
"description": "Canvas table holding the staged rows. Present when rows were staged; list its columns with openfda_dataframe_describe, then reference it in openfda_dataframe_query FROM clauses."
},
"page_omitted": {
"type": "number",
"description": "Records dropped from the requested limit/skip window so the page fit the inline byte budget. Present only when records were dropped. Read them by re-calling with skip advanced by the number of records returned, lower limit for a smaller page (when more than one record came back), pass fields to keep only the paths you need, or pass stage=true to query a bounded drain of the match with openfda_dataframe_query."
},
"totalResults": {
"type": "number",
"description": "Total matching drug approval records in the dataset"
},
"effectiveQuery": {
"type": "string",
"description": "Search filter applied to the Drugs@FDA query, as submitted to openFDA"
},
"projected_fields": {
"type": "array",
"items": {
"type": "string"
},
"description": "The field paths each record in results was projected to, as requested — present only when the call passed fields. Every record also keeps its identity key; any other field is absent because the projection left it out, not because openFDA lacks it. Re-call without fields for whole records."
},
"page_largest_fields": {
"type": "array",
"items": {
"type": "object",
"required": [
"path",
"bytes"
],
"properties": {
"path": {
"type": "string",
"description": "Dotted field path on the record. Fields of array elements share their array path, so patient.drug.openfda covers that block in every drug of the report."
},
"bytes": {
"type": "number",
"description": "Serialized UTF-8 bytes the field holds, totalled across array elements."
}
},
"description": "One field of the oversized record and its serialized size.",
"additionalProperties": false
},
"description": "Present only when page_bytes exceeds the 24000-byte budget, which happens only when the one record on the page exceeds it on its own: that record's largest fields, largest first — the deepest single field that holds the overrun, or else up to three top-level fields. Leave them out with a fields projection to shrink the record."
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}openfda_search_device_clearances — Openfda Search Device Clearances
Search FDA device premarket notifications — 510(k) clearances and PMA approvals. With stage=true, call openfda_dataframe_describe for the staged columns, then openfda_dataframe_query for SQL.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"pathway"
],
"properties": {
"skip": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Number of records to skip for pagination (default 0). openFDA caps pagination at 25000 records; a higher value returns a pagination_limit_reached error."
},
"sort": {
"type": "string",
"pattern": "^\\s*(?=\\S)(?![,:.])(?!.*,(?: *[,:.]|\\s*$))(?!.*[^\\s,] +[^\\s,])(?!.*\\.(?:[.:,\\s]|$))(?!.*:(?!(?:[Aa][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?|[Dd][Ee][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?)(?: *,|\\s*$)))[A-Za-z0-9_.:, ]*[A-Za-z0-9_]\\s*$",
"minLength": 1,
"description": "Sort expression — a field path optionally suffixed with :asc or :desc (any letter case; :ascending and :descending are also accepted, and every direction is sent as asc or desc); comma-separate for multi-field sort. Example: decision_date:desc. Field paths take only letters, digits, underscores, and dots; any other character or direction is rejected before the request. A well-formed but non-sortable field still causes a query error — use a documented field name."
},
"limit": {
"type": "number",
"default": 10,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1-1000, default 10). Serialized record size varies by three orders of magnitude across openFDA endpoints, so the page is also bounded by a 24000-byte serialized budget: a page that would overrun it returns fewer records than requested and reports the cut on page_omitted. Whenever any record matched, at least one comes back, however large it measures — page_bytes then reports its size and page_largest_fields names its largest fields, which fields can project away. A 510(k) record carries a summary narrative and is several times the size of a PMA record."
},
"stage": {
"type": "boolean",
"default": false,
"description": "Stage the matched set on a DataCanvas for SQL analysis — openfda_dataframe_describe lists the staged columns, openfda_dataframe_query runs the SQL. Default false — the call returns one page for one upstream request. When true, records are also drained onto a canvas table up to a size budget (staged_rows reports how many reached it). Staging is for record-level SQL over a bounded slice; for a distribution over everything that matched, openfda_count_values aggregates server-side in one request. Requires CANVAS_PROVIDER_TYPE=duckdb."
},
"fields": {
"type": "array",
"items": {
"type": "string",
"pattern": "^(?!\\.)(?!.*\\.\\.)(?!.*\\.$)[A-Za-z0-9_.]+$"
},
"description": "Dotted record paths to keep on each returned record, e.g. [\"decision_date\", \"applicant\"] — any key path on a returned record works, and openfda_describe_fields lists the searchable ones. Each record is projected to these paths plus its identity key (k_number, pma_number, supplement_number) before the inline byte budget is measured, identically on both response surfaces, and a path through an array applies to every element. Use it when records are large: a page_bytes over the budget comes with the largest fields of the oversized record. A path no returned record carries is named on the notice. Omit for whole records. Paths take only letters, digits, underscores, and dots; anything else is rejected before the request. With stage=true, only the inline page is projected — the staged table keeps its columns."
},
"search": {
"type": "string",
"pattern": "\\S",
"minLength": 1,
"description": "openFDA search query. Examples: applicant:\"medtronic\", advisory_committee_description:\"cardiovascular\", product_code:\"DXN\", openfda.device_name:\"catheter\". Omit to browse recent. Separate clauses with spaces. A \"+\" written in the URL style of openFDA's examples also reads as a space where it separates: beside AND, OR, NOT, or TO, after a closing quote, paren, or bracket, or between a term and the next \"field:\" prefix (a:\"x\"+AND+b:\"y\", [20200101+TO+20201231], c:x+d:y). In a query that marks a clause required with a leading +field:, only the AND/OR/NOT/TO positions read that way. Any other \"+\" is sent as written, and inside a quoted phrase \"+\" is a literal plus sign. Double quotes, parentheses, and range brackets must balance, and the query must not end on a backslash — each is rejected before the request."
},
"pathway": {
"enum": [
"510k",
"pma"
],
"type": "string",
"description": "Premarket pathway. 510(k) is the most common; PMA is for higher-risk devices."
},
"canvas_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{10}$",
"description": "Canvas ID returned by a prior stage=true call to this tool or another openFDA search tool (openfda_search_* or openfda_lookup_ndc). Passing one stages this search onto that canvas (same effect as stage=true) so result sets accumulate for cross-table joins. Omit to stage onto a fresh canvas."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"meta",
"results",
"totalResults"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"meta": {
"type": "object",
"required": [
"total",
"skip",
"limit",
"lastUpdated"
],
"properties": {
"skip": {
"type": "number",
"description": "Pagination offset"
},
"limit": {
"type": "number",
"description": "Page size applied to this request — the requested limit, lowered to the records returned when the 24000-byte inline budget bounded the page (see page_omitted). Not a count of what arrived: a window running past the end of the matched set returns fewer records than this, so read the length of results for the actual count."
},
"total": {
"type": "number",
"description": "Total matching records"
},
"lastUpdated": {
"type": "string",
"description": "Dataset last updated date"
},
"totalUnverified": {
"type": "boolean",
"description": "Present (true) only on an empty page at skip > 0 whose total could not be confirmed: openFDA answers a page past the end and a search that matched nothing alike, and the follow-up request for the total failed. total then reads 0 without ruling out records at a lower skip — re-call with skip=0 to tell."
}
},
"description": "Response metadata",
"additionalProperties": false
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"canvas_disabled",
"canvas_not_found",
"canvas_capacity_exhausted",
"rate_limited",
"upstream_error",
"malformed_search",
"query_error",
"pagination_limit_reached"
],
"description": "Machine-readable failure mode. Declared by this tool: `canvas_disabled`: Staging was requested (stage=true or a canvas_id) but DataCanvas is disabled. `canvas_not_found`: The canvas_id is well-formed but names no active canvas — expired or never minted. `canvas_capacity_exhausted`: canvas_id was omitted and the active canvas cap is already reached. `rate_limited`: The openFDA daily or per-minute request limit is exceeded. `upstream_error`: The openFDA API returned a 5xx server error. `malformed_search`: The search query leaves a double quote, parenthesis, or range bracket unclosed, or ends on a backslash. `query_error`: The search query was rejected by openFDA (malformed field name, invalid syntax). `pagination_limit_reached`: skip exceeds the 25000 record pagination ceiling. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Canvas staging disclosure when the call staged, the fields projection and any requested path no record carries, the byte-budget disclosure when the inline page was bounded or one record exceeds it (with the routes to the withheld records and the largest fields of the oversized record), and guidance when results are empty — how to broaden filters or correct field names."
},
"results": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"description": "510(k) or PMA records — 510(k) carries k_number, device_name, applicant, product_code, decision_date, decision_description, advisory_committee_description; PMA carries pma_number, trade_name, generic_name, supplement_number plus shared applicant/product_code/decision_date/decision_description."
},
"spilled": {
"type": "boolean",
"description": "True when this call staged its matched set on the canvas — use canvas_id with openfda_dataframe_describe for its columns, then openfda_dataframe_query for SQL. Absent when staging was not requested."
},
"canvas_id": {
"type": "string",
"description": "DataCanvas session id for the staged result set. Present when this call staged. Pass to openfda_dataframe_query / openfda_dataframe_describe, or back into this tool to accumulate more tables on the same canvas."
},
"truncated": {
"type": "boolean",
"description": "True when fewer rows reached the canvas than matched upstream — staging stopped at its size budget or openFDA's 25000-row pagination ceiling. Narrow the query (filters, date range) for a complete set."
},
"page_bytes": {
"type": "number",
"description": "Serialized size of results in this response, in UTF-8 bytes. Present when the 24000-byte inline budget bounded the page or when the page exceeds it; larger than the budget only when a single record exceeds it on its own, which is returned whole — page_largest_fields then names its largest fields, and fields projects the record to the paths you need."
},
"staged_rows": {
"type": "number",
"description": "Rows written to the canvas table. Compare with meta.total: a smaller value means staging stopped at its size budget and the table holds only the first staged_rows records."
},
"canvas_table": {
"type": "string",
"description": "Canvas table holding the staged rows. Present when rows were staged; list its columns with openfda_dataframe_describe, then reference it in openfda_dataframe_query FROM clauses."
},
"page_omitted": {
"type": "number",
"description": "Records dropped from the requested limit/skip window so the page fit the inline byte budget. Present only when records were dropped. Read them by re-calling with skip advanced by the number of records returned, lower limit for a smaller page (when more than one record came back), pass fields to keep only the paths you need, or pass stage=true to query a bounded drain of the match with openfda_dataframe_query."
},
"totalResults": {
"type": "number",
"description": "Total matching device clearance records in the dataset"
},
"effectiveQuery": {
"type": "string",
"description": "Search filter applied to the device clearance query, as submitted to openFDA"
},
"projected_fields": {
"type": "array",
"items": {
"type": "string"
},
"description": "The field paths each record in results was projected to, as requested — present only when the call passed fields. Every record also keeps its identity key; any other field is absent because the projection left it out, not because openFDA lacks it. Re-call without fields for whole records."
},
"page_largest_fields": {
"type": "array",
"items": {
"type": "object",
"required": [
"path",
"bytes"
],
"properties": {
"path": {
"type": "string",
"description": "Dotted field path on the record. Fields of array elements share their array path, so patient.drug.openfda covers that block in every drug of the report."
},
"bytes": {
"type": "number",
"description": "Serialized UTF-8 bytes the field holds, totalled across array elements."
}
},
"description": "One field of the oversized record and its serialized size.",
"additionalProperties": false
},
"description": "Present only when page_bytes exceeds the 24000-byte budget, which happens only when the one record on the page exceeds it on its own: that record's largest fields, largest first — the deepest single field that holds the overrun, or else up to three top-level fields. Leave them out with a fields projection to shrink the record."
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}openfda_lookup_ndc — Openfda Lookup Ndc
Look up drugs in the NDC (National Drug Code) Directory. Identify drug products by NDC code, find active ingredients, packaging details, or manufacturer info. Pair with openfda_get_drug_label using the returned brand_name or set_id to read the package insert. With stage=true, call openfda_dataframe_describe for the staged columns, then openfda_dataframe_query for SQL.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"search"
],
"properties": {
"skip": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Number of records to skip for pagination (default 0). openFDA caps pagination at 25000 records; a higher value returns a pagination_limit_reached error."
},
"sort": {
"type": "string",
"pattern": "^\\s*(?=\\S)(?![,:.])(?!.*,(?: *[,:.]|\\s*$))(?!.*[^\\s,] +[^\\s,])(?!.*\\.(?:[.:,\\s]|$))(?!.*:(?!(?:[Aa][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?|[Dd][Ee][Ss][Cc](?:[Ee][Nn][Dd][Ii][Nn][Gg])?)(?: *,|\\s*$)))[A-Za-z0-9_.:, ]*[A-Za-z0-9_]\\s*$",
"minLength": 1,
"description": "Sort expression — a field path optionally suffixed with :asc or :desc (any letter case; :ascending and :descending are also accepted, and every direction is sent as asc or desc); comma-separate for multi-field sort. Example: listing_expiration_date:desc. Field paths take only letters, digits, underscores, and dots; any other character or direction is rejected before the request. A well-formed but non-sortable field still causes a query error — use a documented field name."
},
"limit": {
"type": "number",
"default": 10,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of records to return (1-1000, default 10). Serialized record size varies by three orders of magnitude across openFDA endpoints, so the page is also bounded by a 24000-byte serialized budget: a page that would overrun it returns fewer records than requested and reports the cut on page_omitted. Whenever any record matched, at least one comes back, however large it measures — page_bytes then reports its size and page_largest_fields names its largest fields, which fields can project away. A record grows with its packaging list, so a product with many package configurations is several times the size of one with a single package."
},
"stage": {
"type": "boolean",
"default": false,
"description": "Stage the matched set on a DataCanvas for SQL analysis — openfda_dataframe_describe lists the staged columns, openfda_dataframe_query runs the SQL. Default false — the call returns one page for one upstream request. When true, records are also drained onto a canvas table up to a size budget (staged_rows reports how many reached it). Staging is for record-level SQL over a bounded slice; for a distribution over everything that matched, openfda_count_values aggregates server-side in one request. Requires CANVAS_PROVIDER_TYPE=duckdb."
},
"fields": {
"type": "array",
"items": {
"type": "string",
"pattern": "^(?!\\.)(?!.*\\.\\.)(?!.*\\.$)[A-Za-z0-9_.]+$"
},
"description": "Dotted record paths to keep on each returned record, e.g. [\"brand_name\", \"active_ingredients.name\"] — any key path on a returned record works, and openfda_describe_fields lists the searchable ones. Each record is projected to these paths plus its identity key (product_ndc) before the inline byte budget is measured, identically on both response surfaces, and a path through an array applies to every element. Use it when records are large: a page_bytes over the budget comes with the largest fields of the oversized record. A path no returned record carries is named on the notice. Omit for whole records. Paths take only letters, digits, underscores, and dots; anything else is rejected before the request. With stage=true, only the inline page is projected — the staged table keeps its columns."
},
"search": {
"type": "string",
"pattern": "\\S",
"minLength": 1,
"description": "openFDA search query. Examples: product_ndc:\"0363-0218\", brand_name:\"aspirin\", generic_name:\"metformin\", openfda.manufacturer_name:\"walgreen\", active_ingredients.name:\"ASPIRIN\". Separate clauses with spaces. A \"+\" written in the URL style of openFDA's examples also reads as a space where it separates: beside AND, OR, NOT, or TO, after a closing quote, paren, or bracket, or between a term and the next \"field:\" prefix (a:\"x\"+AND+b:\"y\", [20200101+TO+20201231], c:x+d:y). In a query that marks a clause required with a leading +field:, only the AND/OR/NOT/TO positions read that way. Any other \"+\" is sent as written, and inside a quoted phrase \"+\" is a literal plus sign. Double quotes, parentheses, and range brackets must balance, and the query must not end on a backslash — each is rejected before the request."
},
"canvas_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{10}$",
"description": "Canvas ID returned by a prior stage=true call to this tool or an openFDA search tool (openfda_search_*). Passing one stages this lookup onto that canvas (same effect as stage=true) so result sets accumulate for cross-table joins. Omit to stage onto a fresh canvas."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"meta",
"results",
"totalResults",
"effectiveQuery"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"meta": {
"type": "object",
"required": [
"total",
"skip",
"limit",
"lastUpdated"
],
"properties": {
"skip": {
"type": "number",
"description": "Pagination offset"
},
"limit": {
"type": "number",
"description": "Page size applied to this request — the requested limit, lowered to the records returned when the 24000-byte inline budget bounded the page (see page_omitted). Not a count of what arrived: a window running past the end of the matched set returns fewer records than this, so read the length of results for the actual count."
},
"total": {
"type": "number",
"description": "Total matching records"
},
"lastUpdated": {
"type": "string",
"description": "Dataset last updated date"
},
"totalUnverified": {
"type": "boolean",
"description": "Present (true) only on an empty page at skip > 0 whose total could not be confirmed: openFDA answers a page past the end and a search that matched nothing alike, and the follow-up request for the total failed. total then reads 0 without ruling out records at a lower skip — re-call with skip=0 to tell."
}
},
"description": "Response metadata",
"additionalProperties": false
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"canvas_disabled",
"canvas_not_found",
"canvas_capacity_exhausted",
"rate_limited",
"upstream_error",
"malformed_search",
"query_error",
"pagination_limit_reached"
],
"description": "Machine-readable failure mode. Declared by this tool: `canvas_disabled`: Staging was requested (stage=true or a canvas_id) but DataCanvas is disabled. `canvas_not_found`: The canvas_id is well-formed but names no active canvas — expired or never minted. `canvas_capacity_exhausted`: canvas_id was omitted and the active canvas cap is already reached. `rate_limited`: The openFDA daily or per-minute request limit is exceeded. `upstream_error`: The openFDA API returned a 5xx server error. `malformed_search`: The search query leaves a double quote, parenthesis, or range bracket unclosed, or ends on a backslash. `query_error`: The search query was rejected by openFDA (malformed field name, invalid syntax). `pagination_limit_reached`: skip exceeds the 25000 record pagination ceiling. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Canvas staging disclosure when the call staged, the fields projection and any requested path no record carries, the byte-budget disclosure when the inline page was bounded or one record exceeds it (with the routes to the withheld records and the largest fields of the oversized record), and guidance when results are empty — how to broaden filters or correct field names."
},
"results": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"description": "NDC directory records — product_ndc, brand_name, generic_name, labeler_name, dosage_form, route, marketing_category, active_ingredients[], packaging[], listing_expiration_date."
},
"spilled": {
"type": "boolean",
"description": "True when this call staged its matched set on the canvas — use canvas_id with openfda_dataframe_describe for its columns, then openfda_dataframe_query for SQL. Absent when staging was not requested."
},
"canvas_id": {
"type": "string",
"description": "DataCanvas session id for the staged result set. Present when this call staged. Pass to openfda_dataframe_query / openfda_dataframe_describe, or back into this tool to accumulate more tables on the same canvas."
},
"truncated": {
"type": "boolean",
"description": "True when fewer rows reached the canvas than matched upstream — staging stopped at its size budget or openFDA's 25000-row pagination ceiling. Narrow the query (filters, date range) for a complete set."
},
"page_bytes": {
"type": "number",
"description": "Serialized size of results in this response, in UTF-8 bytes. Present when the 24000-byte inline budget bounded the page or when the page exceeds it; larger than the budget only when a single record exceeds it on its own, which is returned whole — page_largest_fields then names its largest fields, and fields projects the record to the paths you need."
},
"staged_rows": {
"type": "number",
"description": "Rows written to the canvas table. Compare with meta.total: a smaller value means staging stopped at its size budget and the table holds only the first staged_rows records."
},
"canvas_table": {
"type": "string",
"description": "Canvas table holding the staged rows. Present when rows were staged; list its columns with openfda_dataframe_describe, then reference it in openfda_dataframe_query FROM clauses."
},
"page_omitted": {
"type": "number",
"description": "Records dropped from the requested limit/skip window so the page fit the inline byte budget. Present only when records were dropped. Read them by re-calling with skip advanced by the number of records returned, lower limit for a smaller page (when more than one record came back), pass fields to keep only the paths you need, or pass stage=true to query a bounded drain of the match with openfda_dataframe_query."
},
"totalResults": {
"type": "number",
"description": "Total matching NDC records in the dataset"
},
"effectiveQuery": {
"type": "string",
"description": "Search filter applied to the NDC lookup, as submitted to openFDA"
},
"projected_fields": {
"type": "array",
"items": {
"type": "string"
},
"description": "The field paths each record in results was projected to, as requested — present only when the call passed fields. Every record also keeps its identity key; any other field is absent because the projection left it out, not because openFDA lacks it. Re-call without fields for whole records."
},
"page_largest_fields": {
"type": "array",
"items": {
"type": "object",
"required": [
"path",
"bytes"
],
"properties": {
"path": {
"type": "string",
"description": "Dotted field path on the record. Fields of array elements share their array path, so patient.drug.openfda covers that block in every drug of the report."
},
"bytes": {
"type": "number",
"description": "Serialized UTF-8 bytes the field holds, totalled across array elements."
}
},
"description": "One field of the oversized record and its serialized size.",
"additionalProperties": false
},
"description": "Present only when page_bytes exceeds the 24000-byte budget, which happens only when the one record on the page exceeds it on its own: that record's largest fields, largest first — the deepest single field that holds the overrun, or else up to three top-level fields. Leave them out with a fields projection to shrink the record."
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}openfda_drug_profile — Openfda Drug Profile
Resolve one drug name to its FDA identity, then fan out in parallel across the bounded per-drug openFDA endpoints and merge into one profile: identity, label highlights, adverse-event summary, recall history, Drugs@FDA approval, and shortage status. Replaces chaining openfda_get_drug_label, openfda_search_adverse_events, openfda_search_recalls, openfda_search_drug_approvals, and openfda_search_drug_shortages — and reconciles the identifier drift between endpoints that makes that chaining error-prone. Each section is best-effort: a miss returns null rather than failing the call. For deep dives into any one area, use the dedicated tool.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"drug"
],
"properties": {
"drug": {
"type": "string",
"pattern": "\\S",
"minLength": 1,
"description": "Drug name to profile — brand or generic (e.g. \"metformin\", \"Humira\", \"Glucophage\"). Resolved once to an FDA identity; meta.fanOutKey reports the name the recall, approval, and shortage sub-queries searched."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"meta",
"identity",
"label",
"adverse_events",
"recalls",
"recalls_total",
"approval",
"shortage",
"degraded",
"sectionsFound"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"meta": {
"type": "object",
"required": [
"drug",
"resolvedVia",
"fanOutKey"
],
"properties": {
"drug": {
"type": "string",
"description": "The drug name supplied in the request, echoed back."
},
"fanOutKey": {
"type": "string",
"description": "Name the structured sub-queries (recall/enforcement, Drugs@FDA approval, shortage) ran against — the input term when it is whole words of the resolved generic name (an ingredient such as \"metformin\" or \"adalimumab\", so one label's product-specific generic does not narrow them), otherwise the resolved generic name, falling back to a brand name, then the input term. The adverse-event sub-queries instead key off the raw input term to match heterogeneous reporter-entered values."
},
"resolvedVia": {
"enum": [
"label",
"ndc",
"none"
],
"type": "string",
"description": "Which endpoint resolved the canonical identity: drug/label, drug/ndc, or none."
}
},
"description": "Request metadata.",
"additionalProperties": false
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"blank_drug_name"
],
"description": "Machine-readable failure mode. Declared by this tool: `blank_drug_name`: The drug name carries no searchable character — only quotes, backslashes, or whitespace. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"label": {
"anyOf": [
{
"type": "object",
"required": [
"indications",
"warnings",
"dosage"
],
"properties": {
"dosage": {
"type": [
"string",
"null"
],
"description": "Dosage and administration (truncated)."
},
"warnings": {
"type": [
"string",
"null"
],
"description": "Warnings (truncated) — boxed warning or warnings_and_cautions for Rx labels, falling back to the OTC-monograph warnings section."
},
"indications": {
"type": [
"string",
"null"
],
"description": "Indications and usage (truncated)."
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Label highlights from drug/label, or null when no label matched."
},
"notice": {
"type": "string",
"description": "Guidance when the drug could not be resolved or upstream errors degraded the profile. Absent on a fully populated profile."
},
"recalls": {
"type": "array",
"items": {
"type": "object",
"required": [
"classification",
"reason",
"recalling_firm",
"date"
],
"properties": {
"date": {
"type": [
"string",
"null"
],
"description": "Recall initiation or report date (YYYYMMDD)."
},
"reason": {
"type": [
"string",
"null"
],
"description": "Reason for recall (truncated)."
},
"classification": {
"type": [
"string",
"null"
],
"description": "Recall hazard class (Class I/II/III)."
},
"recalling_firm": {
"type": [
"string",
"null"
],
"description": "Firm conducting the recall."
}
},
"description": "A single recall or enforcement action.",
"additionalProperties": false
},
"description": "The newest 5 drug/enforcement recall actions whose openfda.generic_name or product_description matches meta.fanOutKey (may be empty); recalls_total counts every match."
},
"approval": {
"anyOf": [
{
"type": "object",
"required": [
"applicationNumber",
"sponsor",
"marketingStatus"
],
"properties": {
"sponsor": {
"type": [
"string",
"null"
],
"description": "Sponsor/applicant name."
},
"marketingStatus": {
"type": [
"string",
"null"
],
"description": "Derived marketing/approval status."
},
"applicationNumber": {
"type": [
"string",
"null"
],
"description": "NDA/ANDA application number."
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Best-matching Drugs@FDA application whose openfda.generic_name or products.active_ingredients.name matches meta.fanOutKey, or null when none matched or the sub-query failed (listed in degraded)."
},
"degraded": {
"type": "array",
"items": {
"type": "object",
"required": [
"section",
"reason",
"message"
],
"properties": {
"reason": {
"type": "string",
"description": "Machine-readable failure reason (e.g. rate_limited, upstream_error, query_error)."
},
"message": {
"type": "string",
"description": "Upstream failure message for this section, truncated."
},
"section": {
"enum": [
"identity",
"label",
"adverse_events",
"recalls",
"approval",
"shortage"
],
"type": "string",
"description": "Profile section whose sub-query failed."
}
},
"description": "One failed sub-query and why it failed.",
"additionalProperties": false
},
"description": "Sub-queries that failed upstream, empty when every section resolved. A section listed here is unknown, not absent — a null section with no entry here genuinely has no FDA record."
},
"identity": {
"type": "object",
"required": [
"brand_names",
"generic_name",
"product_ndc",
"rxcui",
"spl_set_id"
],
"properties": {
"rxcui": {
"type": [
"string",
"null"
],
"description": "RxNorm concept unique identifier, when available."
},
"spl_set_id": {
"type": [
"string",
"null"
],
"description": "SPL set ID for the drug label, when available."
},
"brand_names": {
"type": "array",
"items": {
"type": "string"
},
"description": "Brand names for the drug (openfda.brand_name)."
},
"product_ndc": {
"type": [
"string",
"null"
],
"description": "Representative product NDC, when available."
},
"generic_name": {
"type": [
"string",
"null"
],
"description": "Generic name (openfda.generic_name) of the resolved label or NDC record — it can name one product (e.g. \"ADALIMUMAB-AACF\"). meta.fanOutKey uses it unless the input term is whole words of it."
}
},
"description": "Identity resolved once and reused across every sub-query to avoid identifier drift.",
"additionalProperties": false
},
"shortage": {
"anyOf": [
{
"type": "object",
"required": [
"status",
"availability"
],
"properties": {
"status": {
"type": [
"string",
"null"
],
"description": "Shortage status (Current, To Be Discontinued, or Resolved)."
},
"availability": {
"type": [
"string",
"null"
],
"description": "Availability note (truncated)."
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Current or most-recent drug shortage status, or null when none on record."
},
"recalls_total": {
"type": [
"number",
"null"
],
"description": "Total drug/enforcement reports the recall search matched — recalls lists the newest 5 of them. Null when the recall sub-query failed (listed in degraded): unknown, not zero."
},
"sectionsFound": {
"type": "number",
"description": "How many profile sections (label, adverse_events, recalls, approval, shortage) returned data."
},
"adverse_events": {
"anyOf": [
{
"type": "object",
"required": [
"total",
"seriousCount",
"topReactions"
],
"properties": {
"total": {
"type": "number",
"description": "Adverse-event reports naming this drug — every matching report, including those with no seriousness recorded; equals openfda_search_adverse_events meta.total for the same medicinalproduct search."
},
"seriousCount": {
"type": "number",
"description": "Reports flagged serious (serious=1). total − seriousCount covers both non-serious reports and those with no seriousness recorded."
},
"topReactions": {
"type": "array",
"items": {
"type": "object",
"required": [
"term",
"count"
],
"properties": {
"term": {
"type": "string",
"description": "MedDRA reaction term."
},
"count": {
"type": "number",
"description": "Reports citing this reaction."
}
},
"description": "A reaction term paired with its report count.",
"additionalProperties": false
},
"description": "Most-frequent reported reactions, descending."
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Adverse-event summary aggregated over drug/event, or null when unavailable."
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}openfda_dataframe_describe — Openfda Dataframe Describe
List the tables and column schemas on a DataCanvas staged by an openFDA search tool. Call before openfda_dataframe_query to discover the exact table name, column names, and DuckDB types needed for valid SQL. row_count is the full staged result set, not the inline preview count. Columns typed JSON hold nested openFDA objects/arrays — query them with DuckDB json functions.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"canvas_id"
],
"properties": {
"canvas_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{10}$",
"description": "Canvas ID from the canvas_id field of an openFDA search tool response (openfda_search_* or openfda_lookup_ndc), present when the search ran with stage=true."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"tables",
"canvas_id"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"canvas_disabled",
"canvas_not_found"
],
"description": "Machine-readable failure mode. Declared by this tool: `canvas_disabled`: DataCanvas is disabled — CANVAS_PROVIDER_TYPE=none. `canvas_not_found`: The canvas_id does not correspond to an active canvas session. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"tables": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"kind",
"row_count",
"columns"
],
"properties": {
"kind": {
"type": "string",
"description": "Object type: \"table\" for staged data, \"view\" for a derived view."
},
"name": {
"type": "string",
"description": "Table or view name — use verbatim in SQL FROM clauses."
},
"columns": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"type"
],
"properties": {
"name": {
"type": "string",
"description": "Column name — use in SELECT, WHERE, GROUP BY, ORDER BY."
},
"type": {
"type": "string",
"description": "DuckDB type (VARCHAR, JSON, BIGINT, DOUBLE, BOOLEAN, ...)."
},
"nullable": {
"type": "boolean",
"description": "True when the column may contain NULL. Absent when unknown."
}
},
"description": "A single column in the table.",
"additionalProperties": false
},
"description": "All columns in this table, in schema order."
},
"row_count": {
"type": "number",
"description": "Total rows in this table — the full staged set, not the inline preview."
}
},
"description": "A registered table or view on the canvas.",
"additionalProperties": false
},
"description": "All tables and views available on this canvas."
},
"canvas_id": {
"type": "string",
"description": "Canvas ID that was described — pass to openfda_dataframe_query."
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": true
}openfda_dataframe_query — Openfda Dataframe Query
Run a read-only SQL SELECT against a DataCanvas table staged by an openFDA search tool (call one with stage=true; its response carries canvas_id + canvas_table). Enables GROUP BY, COUNT/SUM/AVG, time-series, and joins across the staged result set without re-paging the API. Call openfda_dataframe_describe first to get the exact table and column names. Results are capped at the canvas row limit — when truncated is true, page the rest with ORDER BY plus LIMIT/OFFSET. Scalar fields are stored as text (CAST for numeric math); nested objects/arrays are JSON columns — read them with DuckDB json functions, e.g. json_extract_string(openfda, '$.brand_name[0]'). Only SELECT is allowed — DDL, DML, COPY, and file-reading functions are blocked.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"canvas_id",
"query"
],
"properties": {
"query": {
"type": "string",
"pattern": "\\S",
"minLength": 1,
"description": "SQL SELECT against the staged table. Use the table name from openfda_dataframe_describe. Example: \"SELECT classification, COUNT(*) AS n FROM spilled_ab12cd34 GROUP BY classification ORDER BY n DESC\"."
},
"canvas_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{10}$",
"description": "Canvas ID from the canvas_id field of an openFDA search tool response (openfda_search_* or openfda_lookup_ndc), present when the search ran with stage=true."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"rows",
"row_count",
"truncated",
"canvas_id"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"rows": {
"type": "array",
"items": {
"type": "object",
"description": "A result row — keys are the SELECT column names, values the computed data.",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"description": "Rows returned by the query (capped at the canvas row limit)."
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"canvas_disabled",
"canvas_not_found",
"missing_table",
"invalid_query"
],
"description": "Machine-readable failure mode. Declared by this tool: `canvas_disabled`: DataCanvas is disabled — CANVAS_PROVIDER_TYPE=none. `canvas_not_found`: The canvas_id does not correspond to an active canvas session. `missing_table`: The SQL references a table that is not on the canvas — dropped, expired, or mistyped. `invalid_query`: The SQL is not a valid SELECT, references an unknown column, uses a blocked operation, or fails on a staged value (a CAST that does not convert). Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"canvas_id": {
"type": "string",
"description": "Canvas ID that was queried — reuse for follow-up queries."
},
"row_count": {
"type": "number",
"description": "Number of rows in this response. Equals the canvas row limit when truncated is true."
},
"truncated": {
"type": "boolean",
"description": "True when the result hit the canvas row limit and rows beyond it were dropped. Page the rest with ORDER BY plus LIMIT/OFFSET."
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": true
}Prompts
0 advertised
Resources
1 advertised
openfda-guide
Query discipline for the openfda_* tools — gates that stop false zeros and misread counts, where NDCs, application numbers, set_ids, K-numbers, and recall numbers come from, and the chains that join them across endpoints. Use before comparing adverse-event counts, concluding a drug or device has no record, or linking recalls, labels, NDCs, shortages, and clearances.
skill://openfda-guide/SKILL.mdio.modelcontextprotocol/skills
skill://{+path}30-day availability
Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.
- Successful checks
- 1 of 1 (100%)
- Average latency
- 778 ms
- 95th percentile latency
- 778 ms
Maintenance history
Public check and source-refresh events. Editorial and ownership records remain private.
- · probe · passed · 778 ms · metadata changed · 14 tool(s), 1 resource(s), 1 resource template(s), 0 prompt(s)
Connect from your client
Each snippet registers the same endpoint; pick the client you use.
Claude Code
claude mcp add --transport http openfda-mcp-server-caseyjhand https://openfda.caseyjhand.com/mcpCodex
codex mcp add openfda-mcp-server-caseyjhand --url https://openfda.caseyjhand.com/mcpCursor
{
"mcpServers": {
"openfda-mcp-server-caseyjhand": {
"url": "https://openfda.caseyjhand.com/mcp"
}
}
}Cline
{
"mcpServers": {
"openfda-mcp-server-caseyjhand": {
"type": "streamableHttp",
"url": "https://openfda.caseyjhand.com/mcp",
"disabled": false
}
}
}VS Code
{
"servers": {
"openfda-mcp-server-caseyjhand": {
"type": "http",
"url": "https://openfda.caseyjhand.com/mcp"
}
}
}Gemini CLI
gemini mcp add --transport http openfda-mcp-server-caseyjhand https://openfda.caseyjhand.com/mcpClaude Desktop
https://openfda.caseyjhand.com/mcpWindsurf
{
"mcpServers": {
"openfda-mcp-server-caseyjhand": {
"serverUrl": "https://openfda.caseyjhand.com/mcp"
}
}
}Let your agent find its tools
Find servers like openfda-mcp-server from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.