# openFDA Recall Lookup — Drug, Food & Device Enforcement API (`accountable_eel/openfda-recall-lookup`) Actor

Look up FDA recalls by recall number or NDC against the official, free openFDA API, searching drug, food, and device databases in turn. Get classification, reason for recall, recalling firm, distribution, and dates. Pay only for matches.

- **URL**: https://apify.com/accountable\_eel/openfda-recall-lookup.md
- **Developed by:** [Adrian Voss](https://apify.com/accountable_eel) (community)
- **Categories:** Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.40 / 1,000 successful lookups

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## openFDA Recall Lookup

Look up FDA recalls by recall number, drug NDC, or product description against the official,
free [openFDA](https://open.fda.gov) API — classification, reason for recall, recalling firm,
distribution pattern, and dates. Each identifier is checked against the FDA's drug, food, and
device enforcement databases in turn. No API key, no scraping — this hits the official public
API directly.

### Features

- **One identifier, three databases.** A recall number or NDC is checked against drug, food, and
  device enforcement records in sequence, so you don't need to know which category it belongs to.
- **Full enforcement detail.** Classification (Class I/II/III), reason for recall, recalling
  firm, distribution pattern, and both the recall-initiation and report dates.
- **Status & scope.** Recall status, whether it was voluntary or FDA-mandated, and the
  state/country of the recalling firm.
- **Pay only for hits.** Identifiers that don't match any FDA enforcement record cost nothing —
  see [Pricing](#pricing).
- **Built for bulk.** Feed in thousands of identifiers; concurrency, keyword filtering, and
  column selection are all configurable.

### How to use openFDA Recall Lookup — Drug, Food & Device Enforcement API

1. **In the Apify Console.** Open the actor page and click **Start** — the `identifiers` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~openfda-recall-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"identifiers":["D-321-2016"]}'
   ```
3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.

### Input

```json
{
  "identifiers": [
    "D-321-2016"
  ]
}
```

One recall number, drug NDC, or product name/description per line. Checked against FDA drug, food, and device enforcement databases in that order. Accepted formats: D-1234-2023, 0002-1433.

```json
{
  "identifiers": ["D-321-2016", "0002-1433"],
  "testRun": false,
  "onlyFound": false,
  "maxConcurrency": 5
}
```

`identifiers` is a list of recall numbers, drug NDCs, or product names/descriptions — one per
line. Each is checked against the FDA drug, food, and device enforcement databases in that
order. `testRun` limits a run to the first 5 items so you can check your input before spending
on the full list. `onlyFound` hides rows where nothing was found (misses are always free
regardless). Optional `includeKeywords`/`excludeKeywords` filter results by keyword, and
`maxResults` stops the run early once that many results have been found. `columns` lets you pick
which fields to include (all are on by default). `maxConcurrency` (default 5, max 20) caps
parallel requests.

### Output

| query | found | status | category | recallNumber | recallStatus | classification | product | reason | firm | distributionPattern | recallInitiationDate | reportDate | voluntaryMandated | state | country | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| D-321-2016 | true | OK | drug | D-321-2016 | Terminated | Class II | Progesterone 100 mg/mL in Corn Oil Injection, 2 mL vials, Rx only, Essential Wellness PHARMACY, 4625 N. University, Peoria, IL 61614. | Lack of Assurance of Sterility:  A recall of all compounded sterile preparations within expiry is being initiated due to observations associated with poor sterile production practices resulting in a lack of sterility assurance for their finished drugs. | Kalman Health & Wellness, Inc. dba Essential Wellness Pharma | Nationwide | 20150903 | 20151125 | Voluntary: Firm initiated | IL | United States | 2026-08-23T21:03:58.215Z |

One row per identifier, for example:

```json
{
  "query": "D-321-2016",
  "found": true,
  "status": "OK",
  "category": "drug",
  "recallNumber": "D-321-2016",
  "recallStatus": "Terminated",
  "classification": "Class II",
  "product": "Acetaminophen 500mg Tablets, 100-count bottle",
  "reason": "Subpotent (Single Ingredient)",
  "firm": "Example Pharmaceuticals Inc.",
  "distributionPattern": "Nationwide",
  "recallInitiationDate": "2015-11-02",
  "reportDate": "2015-12-10",
  "voluntaryMandated": "Voluntary: Firm Initiated",
  "state": "NJ",
  "country": "United States",
  "scrapedAt": "2026-08-21T12:00:00.000Z"
}
```

An identifier that doesn't match any drug, food, or device enforcement record comes back as
`"found": false` with `status: "NOT_FOUND"` and a plain-English `message` — these rows are never
charged.

### Use cases

- **Supply-chain safety checks.** Verify whether a specific NDC or product lot has an open FDA
  recall before restocking or distributing it.
- **Compliance monitoring.** Bulk-check a catalog of drug/food/device identifiers against active
  enforcement actions.
- **Consumer safety research.** Pull recall reason, classification, and scope for a specific
  incident or product line.
- **Media & watchdog reporting.** Cross-reference recall numbers cited in FDA press releases
  against the underlying enforcement record.
- **Regulatory due diligence.** Confirm the classification severity and distribution pattern of
  a recall before assessing exposure.

### Pricing

$4 per 1,000 identifiers, plus a $0.005 start fee. Misses (`found:false`) are never charged.

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~openfda-recall-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"identifiers":["D-321-2016"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~openfda-recall-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"identifiers":["D-321-2016"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~openfda-recall-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"identifiers":["{{identifier}}"]}`, mapping the row's identifier into the `identifiers` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "FDA Recall Lookup API — Drug, Food, Device Enforcement" — the agent will find and run this actor.

### FAQ

**What counts as "not found"?** An identifier that returns no match against drug (by recall
number and NDC), food, or device enforcement records. This actor tries all of those combinations
before marking a row `NOT_FOUND` — never charged.

**Why does it check multiple databases per identifier?** Recall numbers and NDCs aren't
globally unique across categories, so the actor tries drug recall number, then drug NDC, then
food and device recall numbers, in that order, stopping as soon as one hits.

**Why does the actor issue separate queries instead of one combined search?** OpenFDA's search
syntax reliably 404s when certain typed fields (like `openfda.product_ndc`) are combined with
`OR` in one query — a parser quirk, not a real "not found." Issuing single-field queries in
sequence avoids that failure mode without ever missing a real match.

**How fresh is the data?** Live — every run queries openFDA directly, not a cached snapshot, so
results reflect the FDA's current enforcement records.

**Does this cover every FDA recall?** Coverage is limited to what's indexed in openFDA's
enforcement endpoints for drugs, food, and devices — it doesn't cover other openFDA categories
like adverse events or labeling.

**What proxy should I use?** Apify Proxy is enabled by default; openFDA's public API is
generally permissive, but residential proxies are available if you see blocks on large runs.

# Actor input Schema

## `identifiers` (type: `array`):

One recall number, drug NDC, or product name/description per line. Checked against FDA drug, food, and device enforcement databases in that order. Accepted formats: D-1234-2023, 0002-1433. You're only charged for the ones we actually find — a miss costs nothing.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

## `maxResults` (type: `integer`):

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

## `maxConcurrency` (type: `integer`):

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

## `proxyConfiguration` (type: `object`):

Apify Proxy config. Residential recommended for anti-bot-sensitive targets.

## Actor input object example

```json
{
  "identifiers": [
    "D-321-2016"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "columns": [
    "category",
    "recallNumber",
    "recallStatus",
    "classification",
    "product",
    "reason",
    "firm",
    "distributionPattern",
    "recallInitiationDate",
    "reportDate",
    "voluntaryMandated",
    "state",
    "country"
  ],
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "identifiers": [
        "D-321-2016"
    ],
    "includeKeywords": [],
    "excludeKeywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/openfda-recall-lookup").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "identifiers": ["D-321-2016"],
    "includeKeywords": [],
    "excludeKeywords": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/openfda-recall-lookup").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "identifiers": [
    "D-321-2016"
  ],
  "includeKeywords": [],
  "excludeKeywords": []
}' |
apify call accountable_eel/openfda-recall-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,accountable_eel/openfda-recall-lookup"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/kR575x8h5y9sF3pTX/builds/GoiS4jtnbV3HrwctG/openapi.json
