US-FDA-monitor
Pricing
$4.00 / 1,000 result items
US-FDA-monitor
Query and monitor official U.S. FDA enforcement recalls for drugs, medical devices, and food with normalized, traceable Dataset results.
Pricing
$4.00 / 1,000 result items
Rating
0.0
(0)
Developer
hugo liu
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
US FDA Recall Monitor
Query and monitor official U.S. Food and Drug Administration (FDA) enforcement records for drugs, medical devices, and food. Get normalized, traceable rows from the public openFDA API without writing pagination, filtering, or change-detection code.
Use it for compliance checks, product-safety research, pharmacovigilance workflows, regulatory dashboards, and scheduled recall feeds. Each row keeps the source identity, recall details, official API URL, change metadata, and a stable idempotency key.
Before you run
- For a one-off API call, use
lookuporexport.monitoris stateful and returns only new or changed rows after its snapshot has been established. - Actor usage costs $0.004 per result, equivalent to $4 per 1,000
results, under Pay Per Event pricing. Runs with zero results have no
result-itemusage charge. See the Pricing tab for the current terms. - Data is returned from openFDA at fetch time. This Actor does not provide a real-time freshness guarantee or an SLA for upstream publication delays.
- Start with one product type and a small
maxResults;maxResults: 0can traverse a large result set and is not an unlimited historical archive.
What this Actor does
- Queries the official openFDA
drug/enforcement,device/enforcement, andfood/enforcementdatasets. - Filters by product type, recall number, recalling firm, FDA classification, status, state, keyword, and inclusive report-date windows.
- Normalizes FDA dates such as
YYYYMMDDtoYYYY-MM-DDand flattens selected brand and generic name arrays. - Runs as a one-off current lookup, a bounded export, or an incremental monitor.
- Writes structured records to the run's Apify Dataset, with JSON, CSV, and Excel download views generated from that same Dataset.
- Keeps monitor fingerprints in a named Apify Key-Value Store and emits
ADDEDorUPDATEDrecords for a stable query scope.
Why teams use it
This is a source-first FDA data feed: clean enough for automation, explicit enough for audit trails, and small enough to start with a single bounded query. It uses the government's machine-readable endpoint rather than fragile browser automation, while leaving the original FDA values available for downstream review.
Typical use cases include:
- Monitoring Class I, Class II, or Class III enforcement activity.
- Checking a product category or recalling firm over a defined date window.
- Building a research or compliance dataset for a BI tool or data warehouse.
- Scheduling a recurring monitor that emits only newly seen or changed rows.
Data source and scope
| Product type | Official endpoint | Data returned |
|---|---|---|
drug | api.fda.gov/drug/enforcement.json | Drug enforcement and recall records |
device | api.fda.gov/device/enforcement.json | Medical-device enforcement and recall records |
food | api.fda.gov/food/enforcement.json | Food enforcement and recall records |
The Actor reads public openFDA enforcement data. An upstream openFDA API key is optional and can be supplied for higher limits; it is not included in Dataset output. The Actor does not query adverse-event, labeling, approval, or 510(k) datasets, and it does not independently validate clinical, legal, or product-safety claims.
The source is current at fetch time, but it is not a complete historical
archive owned by this Actor. Use an explicit reportedSince/reportedUntil
window for a reproducible historical export and retain the resulting Dataset
in your own workflow when long-term archival is required.
Request and resource notes
- Selected product-type endpoints are queried sequentially. A multi-type query can therefore make several paginated upstream request series.
maxResultslimits normalized records considered before the monitor diff is applied. Inmonitormode, the number of emitted events can be lower or zero.maxResults: 0removes the Actor-side result cap but still respects the per-endpoint pagination ceiling; it does not fetch an unlimited archive.apiKeychanges the openFDA upstream limit only. It is not an alternative to the Apify API token used to start a run or read a private Dataset.
Run modes
| Mode | What it returns | State behavior |
|---|---|---|
lookup | Matching current source records as CURRENT | Does not read or change monitor state |
export | A bounded current-source result set as CURRENT | Does not read or change monitor state |
monitor | New and changed records as ADDED or UPDATED | Reads and updates the named Key-Value Store |
lookup is useful for a focused check, export for a defined report-date
window, and monitor for a recurring workflow. The default mode is
monitor; when it has no explicit reportedSince, the Actor uses
lookbackDays before today as the fetch window.
Quick start
Apify Console
- Open the Actor and select Input.
- Choose
lookup,export, ormonitor. - Select one or more
productTypesand add a precise filter. - Keep
maxResultssmall for the first run, then click Start. - Open the run's Dataset or use the JSON, CSV, and Excel links in the Output tab.
Apify CLI
After installing and authenticating the Apify CLI, run a bounded lookup:
apify call <ACTOR_ID> \--input '{"mode":"lookup","productTypes":["drug"],"classification":"Class I","maxResults":10}' \--output-dataset
<ACTOR_ID> can be the Actor ID or an authorized Actor name. A valid query
with no matches completes successfully with zero Dataset items.
REST API
Start a run with a JSON body:
curl -X POST \-H "Authorization: Bearer $APIFY_TOKEN" \-H "Content-Type: application/json" \--data '{"mode": "lookup","productTypes": ["food"],"keyword": "allergen","reportedSince": "2025-01-01","reportedUntil": "2025-12-31","maxResults": 100}' \"https://api.apify.com/v2/acts/<ACTOR_ID>/runs?waitForFinish=60"
The response includes the run status and defaultDatasetId. Read the same
Dataset through the Apify API:
GET https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=json&clean=trueGET https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=csv&clean=true&attachment=trueGET https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=xlsx&clean=true&attachment=true
Use limit and offset for pagination. Keep $APIFY_TOKEN in an environment
variable; never put it in Actor input, source code, logs, or Dataset records.
API run lifecycle
The POST request starts an Apify run; it does not return Dataset rows directly.
If waitForFinish=60 returns a non-terminal status such as RUNNING, keep the
returned runId and poll GET /v2/actor-runs/<RUN_ID> or use a Webhook. Read
defaultDatasetId after the run reaches SUCCEEDED. A successful empty Dataset
is valid; in monitor mode it can mean that no record changed, not that the
source had no matching records.
Input API reference
The input is a JSON object. The ./.actor/input_schema.json is the machine-readable contract used by the Console and API.
| Field | Type / default | Description |
|---|---|---|
mode | enum, "monitor" | monitor, lookup, or export. |
productTypes | array; default drug, device, food | FDA enforcement datasets to query. Allowed values are drug, device, and food; the array must not be empty. |
keyword | string | Partial terms matched locally across product description, recall reason, recalling firm, code information, and more-code information. All normalized terms must be present. |
recallNumber | string | Exact recall number match after case and punctuation normalization. |
recallingFirm | string | Case-insensitive partial recalling-firm match. |
classification | enum | Class I, Class II, or Class III. |
status | enum | Ongoing, Completed, or Terminated. |
state | string | Firm state value, normally a two-letter U.S. state code such as NJ. |
reportedSince | YYYY-MM-DD | Inclusive lower bound for report_date. |
reportedUntil | YYYY-MM-DD | Inclusive upper bound for report_date. |
lookbackDays | integer, 14 | Monitor-only lookback when reportedSince is omitted. Range: 0–3650. |
maxResults | integer, 500 | Maximum normalized records considered/emitted. Range: 0–25000; in monitor the final event count can be lower. 0 removes the Actor-side cap within the per-endpoint pagination ceiling. |
emitInitialSnapshot | boolean, true | In monitor, emit first-seen records as ADDED; set false to seed state without emitting the initial snapshot. |
monitorId | string | Optional stable namespace for separate monitors sharing one state store. |
stateStoreName | string, "us-fda-monitor-state" | Named Apify Key-Value Store used for monitor fingerprints. |
apiKey | secret string | Optional openFDA API key for higher upstream limits. It is not written to output. |
Query behavior and validation
- Dates must be real calendar dates in
YYYY-MM-DD; invalid dates such as2026-02-30are rejected. reportedSincecannot be afterreportedUntil.- Filters are sent to openFDA where supported and then applied again after
normalization before
maxResultsis applied. - Product types are queried sequentially. A multi-type query can therefore make one upstream request series per selected product type.
- The Actor paginates each endpoint in bounded batches and stops at its
configured result limit.
maxResults: 0does not mean an unlimited archive; openFDA and the Actor still impose pagination ceilings. - A valid query with no matches returns
SUCCEEDEDwith an empty Dataset. Invalid input, malformed upstream JSON, or an unrecoverable upstream error fails the run with an explanatory message.
Example inputs
Current Class I drug recalls
{"mode": "lookup","productTypes": ["drug"],"classification": "Class I","maxResults": 25}
Historical food enforcement export
{"mode": "export","productTypes": ["food"],"reportedSince": "2015-01-01","reportedUntil": "2015-12-31","maxResults": 1000}
Monitor a topic across all FDA product types
{"mode": "monitor","productTypes": ["drug", "device", "food"],"keyword": "listeria","lookbackDays": 14,"maxResults": 500,"monitorId": "listeria-watch"}
Valid empty result and invalid input
An exact lookup for a number that is not in the selected source is a valid request and returns an empty Dataset:
{"mode": "lookup","productTypes": ["drug"],"recallNumber": "not-a-real-recall","maxResults": 1}
An input such as {"reportedSince":"2026-02-30"} is rejected before an
upstream request because the date is not a real calendar date.
For abnormal upstream behavior, the Actor retries bounded transient failures
such as 429, 5xx, network errors, and timeouts. Malformed JSON and other
unrecoverable responses fail the run with an explanatory error; they are not
silently converted to an empty Dataset.
Output API reference
Each Dataset item is one normalized FDA enforcement record. Missing source
values are represented by an empty string, an empty array, or null for date
fields, according to the field contract.
Representative lookup output
The following is a trimmed example showing the output shape; the complete row contains every field listed below. Hash values are placeholders for the actual 64-character values generated by the run.
{"source": "fda","sourceRecordId": "drug:D-1234-2026","recordType": "recall","changeType": "CURRENT","changedFields": [],"productType": "drug","recallNumber": "D-1234-2026","title": "Example Drug 10mg Tablets","productDescription": "Example Drug 10mg Tablets, 100-count bottle","recallingFirm": "Example Pharma Inc.","classification": "Class I","status": "Ongoing","reasonForRecall": "Failed dissolution specifications","reportDate": "2026-01-15","brandNames": ["Example"],"genericNames": ["Example drug"],"sourceUrl": "https://api.fda.gov/drug/enforcement.json?search=recall_number:D-1234-2026","detectedAt": "2026-09-02T00:00:00.000Z","contentHash": "<sha256>","idempotencyKey": "<sha256>"}
For monitor, the same content fields are accompanied by ADDED or
UPDATED; changedFields lists normalized fields that changed since the prior
snapshot. CURRENT is used by lookup and export.
Identity and monitor metadata
| Field | Type | Description |
|---|---|---|
source | string | Always fda. |
sourceRecordId | string | Stable identity formed from product type and the FDA recall_number or event_id. |
recordType | string | Always recall for this Actor. |
changeType | string | ADDED, UPDATED, or CURRENT. |
changedFields | string[] | Normalized fields changed since the previous monitor snapshot; empty for ADDED and CURRENT. |
detectedAt | ISO timestamp | Time this Actor produced the row. |
contentHash | string | SHA-256 hash of the normalized source snapshot, excluding derived monitor metadata. |
idempotencyKey | string | Stable key for monitor scope, source record, and content version. Use it to deduplicate retries. |
FDA record fields
| Field | Type | Description |
|---|---|---|
productType | string | drug, device, or food. |
recallNumber | string | FDA recall number when supplied. |
eventId | string | FDA event identifier when supplied. |
title | string | Short title derived from the product description, capped at 240 characters. |
productDescription | string | FDA product description. |
recallingFirm | string | Firm initiating or named in the recall. |
city | string | Firm city. |
state | string | Firm state. |
country | string | Firm country. |
classification | string | FDA recall classification. |
status | string | FDA recall status. |
voluntaryMandated | string | FDA voluntary/mandated value. |
distributionPattern | string | Reported distribution pattern. |
productQuantity | string | Reported quantity. |
reasonForRecall | string | FDA reason for recall. |
recallInitiationDate | string or null | Normalized initiation date in YYYY-MM-DD. |
centerClassificationDate | string or null | Normalized FDA center classification date. |
terminationDate | string or null | Normalized termination date when supplied. |
reportDate | string or null | Normalized FDA report_date. |
codeInfo | string | Product lot, code, or identifying information. |
moreCodeInfo | string | Additional code information. |
brandNames | string[] | Brand names from the source's openfda object when supplied. |
genericNames | string[] | Generic names from the source's openfda object when supplied. |
sourceUrl | string | Official openFDA endpoint/query URL used for the record. |
Dataset and output links
The Actor pushes structured rows only. Apify derives these representations from the same Dataset:
- JSON:
.../items?format=json&clean=true - CSV:
.../items?format=csv&clean=true&attachment=true - Excel:
.../items?format=xlsx&clean=true&attachment=true
For non-empty runs, this Actor names the current Dataset
us-fda-monitor-results-<runId> after all rows are delivered. The Dataset ID
does not change, so the Output links remain valid. Empty runs are not renamed
into a long-lived named Dataset.
The other Actors in this product family do not automatically rename their Datasets. If you use all three, retain the returned Dataset IDs immediately or copy/name the results according to your account's retention policy.
Monitoring and delivery semantics
In monitor mode:
- Records are normalized and deduplicated by
sourceRecordId. - The Actor compares the normalized content with the snapshot in
stateStoreName. - New records become
ADDED; changed records becomeUPDATEDwithchangedFields. - Dataset delivery completes before the checkpoint is written.
This is safe at-least-once delivery. If Dataset delivery succeeds but the
checkpoint write fails, a later run may emit the same event again; downstream
consumers should deduplicate with idempotencyKey. Records that disappear from
a later rolling source window are not emitted as REMOVED, because absence may
reflect pagination or the selected date/filter scope rather than an official
withdrawal.
For scheduled monitoring, keep the same monitorId, stateStoreName, and
query scope. The rolling date calculated from lookbackDays is not itself used
to create a new state namespace every day.
The HTTP layer starts requests serially within one process, waits one second by
default between request starts, retries transient network errors, 429, and
5xx responses with bounded exponential backoff and jitter, and honors
Retry-After when supplied. These locks and limits are process-local; they do
not provide account-wide rate limiting or cross-container exactly-once
delivery.
Do not interpret an empty monitor Dataset as a negative safety result. Use
lookup or export when the application needs the current matching records on
every call.
Reliability and limitations
- openFDA fields and availability can change. The Actor preserves the source values it maps, but it does not create a complete FDA data warehouse.
- A broad multi-product or uncapped query can make many upstream requests and
consume more runtime and platform resources. Start with one product type,
one date window, and a small
maxResults. - The Actor reports FDA enforcement data; it does not determine whether a product is clinically safe, whether a remedy was completed, or whether a recall applies to a particular person or inventory item.
- Public data and API availability are subject to FDA/openFDA terms and operational limits. Review the current source guidance before operating at scale.
Local development
Requirements: Node.js 20 or newer.
npm installnpm testnpm run buildapify validate-schema
npm test includes simulated 429/Retry-After, 5xx, timeout, rate-limit,
state-order, and delivery-failure cases. FDA currently has no real-source
test:real script in this package; use a bounded apify run or API smoke when
you need to verify a live current record, historical record, empty result, or
source response. Do not use an unbounded export for smoke testing.
Attribution
This Actor consumes public FDA data through openFDA. Preserve sourceUrl,
productType, and recallNumber when passing records downstream so users can
inspect the authoritative source context.