# FDA NDC Pharmaceutical Labeler & Product Intelligence (`afkdigital/fda-ndc-pharmaceutical-labeler-product-intelligence`) Actor

Extract official FDA NDC prescription and OTC product listings with labeler filters, full packaging and ingredient arrays, and JSON/CSV exports. One row per unique product.

- **URL**: https://apify.com/afkdigital/fda-ndc-pharmaceutical-labeler-product-intelligence.md
- **Developed by:** [AFK Digital](https://apify.com/afkdigital) (community)
- **Categories:** Business, Other
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$6.50 / 1,000 unique ndc products

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

## FDA NDC Pharmaceutical Labeler & Product Intelligence

Extract structured pharmaceutical product listings from the official openFDA NDC Directory API. Filter prescription and OTC products by product-level marketing start date, product type, dosage form and labeler. Deliver one unique product per row with complete packaging and active ingredient arrays, source links and UTC retrieval timestamps.

### Quick start

Run with the default input; no openFDA key is needed for the tested example:

```json
{"maxResults":20,"maxChargeUsd":1}
```

Twenty products cost **$0.13** in product events. The Actor stops at the earliest result or spending limit. The default example is designed to finish within five minutes; availability depends on the upstream API.

Prescription tablet example:

```json
{
  "productType":"HUMAN PRESCRIPTION DRUG",
  "dosageForm":"TABLET",
  "marketingStartDateFrom":"2025-01-01",
  "marketingStartDateTo":"2026-10-29",
  "maxResults":100,
  "maxChargeUsd":0.65
}
```

OTC example:

```json
{"productType":"HUMAN OTC DRUG","maxResults":50,"maxChargeUsd":0.325}
```

For an exact labeler query, use its full FDA spelling, for example `Padagis Israel Pharmaceuticals Ltd`. All text filters are exact, case-sensitive matches and combine with AND. Dates are inclusive and accept YYYY-MM-DD or YYYYMMDD. They describe the labeler's reported marketing start, not when the record was discovered or published. The unfiltered source also includes bulk ingredients and drugs for further processing; use productType to select your scope.

### Output

JSON retains missing values as `null` and preserves every object and field in `packaging` and `active_ingredients`, including strengths, package NDCs, dates and sample flags.

| Field | Meaning |
| --- | --- |
| product_ndc | Product identifier, retained as a string |
| generic_name, brand_name | Names reported in the listing, or null |
| labeler_name | Listing labeler; no physical manufacturer inference |
| dosage_form, product_type | FDA source values |
| marketing_start_date | Product-level source date, YYYYMMDD, or null |
| packaging | Complete array of package objects, or null |
| packaging_description | Array of descriptions aligned with packages; missing descriptions are null |
| active_ingredients | Complete array of ingredient objects, or null |
| packaging_text, active_ingredients_text | Readable joined text for CSV/workflows |
| packaging_json, active_ingredients_json | JSON strings for lossless roundtrip from CSV |
| source_url | Product lookup on official FDA API, without credentials |
| fetched_at | UTC ISO 8601 retrieval timestamp |
| source_last_updated | FDA dataset metadata timestamp |

One row represents a product, not an individual package. No package explosion or ingredient truncation occurs. If the live source repeats a product NDC during a run, the first complete source record encountered is retained; later duplicates are skipped. This is not a historical revision archive. Source changes during traversal can affect coverage.

Export JSON for nested arrays. Use the **Unique NDC products** Dataset view for readable CSV. Select the JSON string columns to keep nested data losslessly in CSV; parse them with JSON.parse in n8n or a Clay transformation. CSV empty cells correspond to missing values; JSON is authoritative for distinguishing null from empty strings.

### Clay, n8n and API use

Run this Actor with JSON input using Apify's integration or an HTTP request. Read the returned defaultDatasetId and request:

```text
GET https://api.apify.com/v2/datasets/DATASET_ID/items?format=json&clean=true
GET https://api.apify.com/v2/datasets/DATASET_ID/items?format=csv&clean=true&view=overview
```

Use an Authorization Bearer header for private datasets. Never put Apify credentials into shareable URLs. Upsert into Clay or a database using product_ndc. Each new Actor run is billed independently; deduplication is within a run, including already saved IDs recovered after a restart.

The output panel also links to RUN_SUMMARY, which records saved product counts, duplicate/invalid skips, pages, request attempts, stop reason and source update date. Source failure after partial progress leaves delivered products accessible and marks the run FAILED.

### Pricing and spending limits

**Pay per event: $0.0065 per successfully saved unique product ($6.50 / 1,000).** The only priced event is `apify-default-dataset-item`. No startup, HTTP request, retry, package or ingredient event is charged. Platform usage is covered by the developer in standard runs. No subscription or rental fee is configured.

The default product budget is $1 and default result limit is 20. The Actor floors the budget to whole products, respects Apify's per-run maximum charge, and stops when another product cannot fit. Hard caps: 10,000 products, $65 product budget, 900 FDA attempts and 240 seconds collection time. Users may lower all caps. Zero product budget returns no products and makes no FDA request.

Only saved product rows are billed; error metadata and summaries are not dataset products. Restart recovery reads existing product IDs and skips them. HTTP retries do not write or bill rows.

### API limits and reliability

The official endpoint is https://api.fda.gov/drug/ndc.json. Current live validation accepted limit=1000; default page size is 100. Some older endpoint documentation still says 100. The general query documentation specifies 1,000 and skip up to 25,000. This Actor follows FDA's trusted HTTPS `Link: rel=next` search_after cursor, rather than using skip to cross the 26,000-hit window.

Published FDA quotas: without a key, 240 requests/minute/IP and 1,000/day/IP; with a key, 240/minute/key and 120,000/day/key. Documentation contains mixed wording about key requirements; the default was verified without a key. Shared Apify IP usage can exhaust anonymous quotas. An optional secret apiKey uses HTTP Basic authentication; it is excluded from output URLs and application logs.

Requests run sequentially at 120/minute by default, capped at 180/minute. Timeouts default to 30 seconds; transient HTTP 408/429/5xx and network failures get up to four retries with exponential backoff, jitter and Retry-After handling. Quotas are never bypassed. No browser, Playwright or proxy is used. Empty searches return an empty dataset; unexpected responses and exhausted safety limits fail visibly.

### Interpretation and source rights

A labeler may be a manufacturer, repackager, relabeler or entity distributing under its own label. **This Actor does not identify verified physical manufacturers or contract packagers.**

**An NDC listing is not FDA product approval.** FDA has not necessarily verified submitted listing information. Do not infer safety, effectiveness, reimbursement eligibility or commercial availability from a row. This product supports data research and workflow enrichment, not medical decisions.

openFDA data is generally public domain/CC0 unless otherwise marked, including commercial reuse subject to its terms and third-party rights. This Actor adds filtering, preservation, export and delivery; it does not claim exclusive ownership of FDA data or FDA endorsement.

Sources:

- https://open.fda.gov/apis/drug/ndc/
- https://open.fda.gov/apis/query-parameters/
- https://open.fda.gov/apis/paging/
- https://open.fda.gov/apis/authentication/
- https://open.fda.gov/license/
- https://open.fda.gov/terms/

### Development

Python 3.13; official Apify Python SDK; stdlib HTTP/JSON.

```bash
python -m unittest discover -s tests -v
python -m src.main
```

See INPUT.json for local input. Store publication and release evidence are maintained separately from the product source.

# Actor input Schema

## `marketingStartDateFrom` (type: `string`):

Inclusive product-level marketing start date, YYYY-MM-DD or YYYYMMDD. Not the date added to FDA.

## `marketingStartDateTo` (type: `string`):

Inclusive upper bound. Omit either bound for an open-ended range.

## `productType` (type: `string`):

Exact FDA value, e.g. HUMAN PRESCRIPTION DRUG, HUMAN OTC DRUG, BULK INGREDIENT or DRUG FOR FURTHER PROCESSING. Omit to include all source product types.

## `dosageForm` (type: `string`):

Exact FDA value, e.g. TABLET, CAPSULE or SOLUTION. Case-sensitive.

## `labelerName` (type: `string`):

Exact full labeler name. A labeler is not necessarily the physical manufacturer or a contract packager.

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

One row per unique product_ndc. Default 20. Hard maximum 10,000; further limited by budgets.

## `maxChargeUsd` (type: `number`):

Stops before the next $0.0065 product would exceed this cap or the platform spending limit. No startup charge.

## `apiKey` (type: `string`):

Optional key for your own FDA quota. Secret field; never included in output URLs or logs. Default example works without it subject to shared-IP quotas.

## `pageSize` (type: `integer`):

100 by default; current endpoint accepts up to 1,000. Smaller values can be used to test pagination.

## `maxRequests` (type: `integer`):

Includes retries. Default 100, hard cap 900. Shared IP daily quotas may still be exhausted by other runs.

## `requestsPerMinute` (type: `integer`):

Sequential request pacing; default 120, hard maximum 180 (below FDA 240/min). Concurrent runs share quotas.

## `requestTimeoutSecs` (type: `integer`):

Bounded timeout per HTTP attempt.

## `maxRetries` (type: `integer`):

Exponential backoff with jitter for HTTP 408/429/5xx and transient network errors. Retries are free.

## `maxRuntimeSecs` (type: `integer`):

Internal collection deadline, at most 240 seconds. Exhausted source/network/request limits fail visibly; saved data stays accessible.

## Actor input object example

```json
{
  "maxResults": 20,
  "maxChargeUsd": 1,
  "pageSize": 100,
  "maxRequests": 100,
  "requestsPerMinute": 120,
  "requestTimeoutSecs": 30,
  "maxRetries": 4,
  "maxRuntimeSecs": 240
}
```

# Actor output Schema

## `products` (type: `string`):

No description

## `csv` (type: `string`):

No description

## `summary` (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 = {
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("afkdigital/fda-ndc-pharmaceutical-labeler-product-intelligence").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 = { "maxResults": 20 }

# Run the Actor and wait for it to finish
run = client.actor("afkdigital/fda-ndc-pharmaceutical-labeler-product-intelligence").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 '{
  "maxResults": 20
}' |
apify call afkdigital/fda-ndc-pharmaceutical-labeler-product-intelligence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,afkdigital/fda-ndc-pharmaceutical-labeler-product-intelligence"
        }
    }
}
```

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/wjS7wLngGZrSxXOap/builds/ankNFC7BEIS2nlY6J/openapi.json
