# Fda 510k Medical Devices Recalls Enforcement Scraper (`arezdev/fda-510k-medical-devices-recalls-enforcement-scraper`) Actor

Search official openFDA records for 510(k) device decisions and medical device, drug or food recalls. Filter by date, company, product or state. Export company details, classifications and source links for regulatory research and monitoring.

- **URL**: https://apify.com/arezdev/fda-510k-medical-devices-recalls-enforcement-scraper.md
- **Developed by:** [Arezdev](https://apify.com/arezdev) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 fda record extracteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

**Find medical device applicants, regulatory contacts, and classified product recalls directly from official FDA records.**

Build MedTech prospect lists from 510(k) decisions or monitor enforcement recalls across medical devices, drugs, and food. Receive flat, exportable JSON with company names, published contacts, product descriptions, classifications, dates, addresses, and source links. Requests use the official openFDA API with no browser, proxy, or API key configuration.

Best for tracking 510(k) decisions and classified enforcement recalls. Choose one of the four supported datasets and a recent date range. Warning letters are outside this Actor's coverage, and source updates can lag the underlying event.

### Ready-to-run example

The Input form includes public example values. A fresh `apify run` uses the input schema defaults. To explicitly select the small sample, including when input was saved by an earlier local run, use:

```sh
apify run --input-file .actor/input.example.json
```

You can also paste [.actor/input.example.json](.actor/input.example.json) into the JSON input editor in Apify Console. Replace the example sources with your own targets and increase the limits when you are ready.

### How to use

1. Choose a dataset: device 510(k) clearances, device recalls, drug recalls, or food recalls.
2. Select an inclusive date range. Optionally add a company/product phrase or company state code.
3. Set the maximum number of saved records and run the Actor. The prefilled example saves 3 records.
4. Download JSON, CSV, or Excel from the dataset, or connect its API to your workflow.

Results are requested newest first, 100 records per API page. Pagination is sequential to respect the public API's rate limits and avoid fetching unnecessary pages. A run can save up to 5,000 records on a paid Apify plan, subject to the event budget and matching data.

### What data can you extract?

| Mode | Official source | Included data |
| --- | --- | --- |
| `DEVICE_510K` | `device/510k.json` | K number, applicant, published regulatory contact, device, specialty, FDA decision, decision date, address |
| `DEVICE_RECALLS` | `device/enforcement.json` | Recall number, recalling firm, device description, hazard class, status, recall reason, report date, address |
| `DRUG_RECALLS` | `drug/enforcement.json` | Drug recall number, recalling firm, description, hazard class, status, reason, report date, address |
| `FOOD_RECALLS` | `food/enforcement.json` | Food recall number, recalling firm, description, hazard class, status, reason, report date, address |

Each record contains exactly 18 flat fields. `status` describes extraction (`SUCCESS` or `ERROR`); `decisionOrRecallStatus` contains the FDA decision or recall status. Missing source values are empty strings. `contactPersonName` comes only from a published `contact` field, typically in 510(k) records. Enforcement records generally do not contain a contact person.

`actionDate` is the **decision date** for 510(k) records and the **enforcement report date** for recalls, normalized to `YYYY-MM-DD`. A recall's report date may differ from its initiation date. `reasonForRecallOrSummary` contains the recall reason, or the FDA's summary/statement availability indicator for 510(k) records; it does not download a summary PDF. `medicalSpecialtyOrClassification` contains the device specialty or the recall hazard classification, such as Class I or Class II.

This Actor queries the four endpoints above. Warning letters, inspection findings, PMA approvals, and summary PDFs are outside these datasets. The 510(k) dataset can include different decision codes; inspect `decisionOrRecallStatus` when selecting prospects. The historical local folder name does not change the available modes.

### Input settings

| Setting | Default | Details |
| --- | --- | --- |
| `datasetMode` | `DEVICE_510K` | One of the four modes above |
| `searchKeyword` | Empty | Literal phrase, up to 200 characters, searched across applicant/product/contact for 510(k), or firm/product/reason for recalls |
| `startDate` | `2026-01-01` | Inclusive decision/report date; real calendar date required |
| `endDate` | `2026-10-15` | Inclusive end date, on or after start date |
| `stateFilter` | Empty | Exact FDA company address state/province code, normalized to uppercase; does not filter recall distribution |
| `maxItems` | `100` | Integer 1–5,000; prefill 3; includes successful and error rows |
| `timeoutSecs` | `15` | Integer 3–60; timeout per HTTP attempt |

```json
{
  "datasetMode": "DEVICE_510K",
  "startDate": "2026-01-01",
  "endDate": "2026-10-15",
  "maxItems": 3,
  "timeoutSecs": 15
}
```

Keyword and state values are escaped as literal Lucene phrases. Wildcards and advanced query expressions are not executed. Empty filters are omitted. Keep the keyword empty for the broadest sample; there is no guaranteed match for an arbitrary phrase, date range, or state.

### Output example

The following illustrative record shows the output shape; it is not a claim about a real FDA clearance.

```json
{
  "recordType": "DEVICE_510K",
  "status": "SUCCESS",
  "referenceNumber": "K260001",
  "companyName": "Example Medical Devices Inc.",
  "contactPersonName": "Jordan Smith",
  "productOrDeviceName": "Example Monitoring System",
  "medicalSpecialtyOrClassification": "Cardiovascular",
  "decisionOrRecallStatus": "Substantially Equivalent",
  "reasonForRecallOrSummary": "Summary",
  "actionDate": "2026-02-05",
  "streetAddress": "100 Example Avenue, Suite 200",
  "city": "Boston",
  "state": "MA",
  "zipCode": "02110",
  "country": "US",
  "fdaSourceUrl": "https://api.fda.gov/device/510k.json?search=k_number.exact%3A%22K260001%22&limit=1",
  "scrapedAt": "2026-10-20T12:00:00.000Z",
  "errorMessage": ""
}
```

All strings are sanitized for spreadsheet export, including removal of null bytes and protection against formula injection. Numeric values and valid phone-like strings are preserved. Consumers displaying these fields in a webpage must render them as text or apply their normal context-specific output encoding.

### Use the results in Clay, n8n, Make, or your API

Use Apify's integration or HTTP modules in Clay, n8n, and Make to run the Actor with the JSON input above. Retrieve the default dataset after completion and filter on `status = SUCCESS`. Use `referenceNumber` together with `recordType` as the key for upserts across scheduled runs. Recalls can change status over time, so update existing rows rather than assuming each reference is immutable.

To read a completed dataset, request `GET https://api.apify.com/v2/datasets/{datasetId}/items?format=json&clean=true` using the dataset ID from the run. Use your integration's secure credential configuration when authentication is required. A run-completion webhook can trigger enrichment, CRM import, Slack alerts, or a compliance review workflow. This Actor itself does not send messages or perform contact enrichment.

### Pricing and limits

The custom pay-per-event name is **`fda-record-scraped`**. Only successfully saved `SUCCESS` rows receive that custom event. **`ERROR` rows cost $0 in custom event fees**, and a valid query with no matches saves no rows and charges no custom events. Apify infrastructure/storage or other platform fees may apply separately.

For a concrete example, at a configured event price of **$0.001 per successful record**, 1,000 successful records cost **$1.00 in event fees**. A run saving 900 successful records and 100 error records costs **$0.90 in event fees** at that rate. These examples illustrate the calculation; the active Store pricing determines the actual rate. Before publishing, configure the event and price in Apify Console. Repository metadata does not activate monetization.

Hosted runs for non-paying Apify users have a hard cap of **25 saved rows**, counting both success and error rows. Local runs do not have this Free plan cap. Both the input limit and the remaining event budget are enforced before saving. When the event budget is exhausted, extraction stops cleanly. LIMITED_PERMISSIONS is supported; the Actor never queries `/v2/users/me`.

No proxies are used. The [openFDA access limits](https://open.fda.gov/apis/authentication/) document 240 requests/minute and 1,000 requests/day per IP without a key; limits and shared datacenter IP quotas can affect availability. Requests are spaced at least 300 ms apart. Transient 429/5xx and network failures receive at most two retries with bounded backoff. A 403 is reported safely without attempting proxy rotation. JSON responses are capped at 2 MiB after decompression. Network errors become uncharged error rows instead of failing the run; only invalid user input exits with code 1.

### FAQ and troubleshooting

**Why are there no results?** Check the dates, selected mode, literal keyword, and company state code. An openFDA `NOT_FOUND` response means the query has no matches and is handled as a successful empty run.

**Why is the contact name empty?** The FDA may not publish a contact. The Actor does not infer names or enrich them from third-party sources.

**Does this include every recall announcement?** It uses classified FDA enforcement reports. Dataset publication timing and coverage follow the [official openFDA enforcement documentation](https://open.fda.gov/apis/drug/enforcement/). It does not monitor press releases or replace the underlying FDA record.

**Can I search a company's full name?** Yes. Enter the phrase as plain text. Keywords search multiple relevant fields, and punctuation is escaped. Narrow phrases may return fewer matches than a single distinctive word.

**What do error rows mean?** `errorMessage` contains a safe failure category such as `HTTP_429`, `ETIMEDOUT`, `INVALID_JSON`, or `RESPONSE_TOO_LARGE`. A page failure stops further pagination while preserving already saved records. No raw response, internal path, stack trace, or credential is written to an error row.

**What happens on cancellation?** Aborting, SIGTERM, and SIGINT cancel in-flight requests and flush buffered rows. The writer serializes dataset writes and enforces the remaining billing budget during shutdown as well.

**Can I request more than 5,000 rows?** Split work across narrower date windows and deduplicate by mode and reference number. This keeps each run within its configured result and event budget. See the [official query parameters](https://open.fda.gov/apis/query-parameters/) and [510(k) field reference](https://open.fda.gov/apis/device/510k/searchable-fields/) for source semantics.

### Local development and deployment

Use Node.js 22 or later. Run `npm ci`, `npm run build`, and `npm test`. Run `npm run smoke` for live, read-only checks of all four FDA endpoints with the default date window. The smoke checks do not push datasets or bill events. Run `npm run smoke:entrypoint` to verify the actual SDK entrypoint in isolated temporary local storage, including all four modes, a no-match query, input rejection, exact row counts, and process memory. These local runs do not bill platform events. Tests use `node:test` and injected transports for deterministic security, streaming, retry, pagination, and billing coverage. Child runners set `UV_THREADPOOL_SIZE=64` and `NODE_ENV=production`.

For a local Actor run, save the example JSON to `storage/key_value_stores/default/INPUT.json`, then run `npm start`. Run from a directory that does not contain environment files. Production deployment uses the included multi-stage `node:22-alpine` Dockerfile, a non-root user, 256 MiB default memory, and a 3,600-second run timeout. The seven input settings are intentionally limited to the FDA use case; page fetching is sequential and has no browser or proxy configuration.

# Actor input Schema

## `datasetMode` (type: `string`):

Choose 510(k) device clearances or classified enforcement recalls for devices, drugs, or food.

## `searchKeyword` (type: `string`):

Optional literal phrase, up to 200 characters. Matches company, product, contact (510(k)), or recall reason. Lucene expressions are not supported.

## `startDate` (type: `string`):

Inclusive decision date for clearances, or report date for recalls, in YYYY-MM-DD format.

## `endDate` (type: `string`):

Inclusive end date, in YYYY-MM-DD format. Must be on or after the start date.

## `stateFilter` (type: `string`):

Optional exact FDA state code, such as CA, MA, or TX. This filters the company address, not recall distribution.

## `maxItems` (type: `integer`):

Includes successful and error records. Apify Free plan runs are capped at 25 saved records. The prefilled sample saves 3 records.

## `timeoutSecs` (type: `integer`):

Timeout per HTTP attempt. Transient errors receive up to two delayed retries without proxies.

## Actor input object example

```json
{
  "datasetMode": "DEVICE_510K",
  "startDate": "2026-01-01",
  "endDate": "2026-10-15",
  "maxItems": 3,
  "timeoutSecs": 15
}
```

# Actor output Schema

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

Default dataset items. Review each record status before using extracted data downstream.

# 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 = {
    "datasetMode": "DEVICE_510K",
    "maxItems": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("arezdev/fda-510k-medical-devices-recalls-enforcement-scraper").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 = {
    "datasetMode": "DEVICE_510K",
    "maxItems": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("arezdev/fda-510k-medical-devices-recalls-enforcement-scraper").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 '{
  "datasetMode": "DEVICE_510K",
  "maxItems": 3
}' |
apify call arezdev/fda-510k-medical-devices-recalls-enforcement-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arezdev/fda-510k-medical-devices-recalls-enforcement-scraper"
        }
    }
}
```

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/Ibwiijr0FgIjZ0JSw/builds/mQJoheD3lcEC7n9gu/openapi.json
