# China NMPA UDI Daily Updates API (`automa-flow/nmpa-udi-daily-updates`) Actor

Get medical-device records from China NMPA UDI daily and weekly release files. Preview 100 records or import a complete release, with package integrity checks and structured fields for your ERP or device database.

- **URL**: https://apify.com/automa-flow/nmpa-udi-daily-updates.md
- **Developed by:** [Vadim Bezrukov](https://apify.com/automa-flow) (community)
- **Categories:** Developer tools, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 udi records

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

## China NMPA UDI Daily Updates API

Keep your medical-device records up to date with China NMPA UDI daily and weekly releases. Download a release as structured rows with device identifiers, product names and registration details, or preview 100 records before a full import.

This Actor is an **independent tool** that consumes NMPA's public release packages. It is **not** affiliated with NMPA and is **not** the official NMPA shared API.

The first published version is described in the [changelog](https://apify.com/automa-flow/nmpa-udi-daily-updates/changelog).

### Preview 100 records

```json
{
  "releaseType": "daily",
  "latestOnly": true,
  "maxPackages": 1,
  "outputMode": "preview",
  "previewRecords": 100
}
```

The Actor downloads and **MD5-verifies the full official package**, then emits only the first 100 records with `output_scope=PREVIEW`. Use the preview to check field names and values before importing a complete release.

### Use from AI agents with MCP

Direct tool URL (authenticated OAuth or API token required to run):
`https://mcp.apify.com?tools=automa-flow/nmpa-udi-daily-updates`

**Select this Actor when** an agent must refresh an ERP/WMS/device-master cache from the official NMPA UDI daily or weekly release feed, with MD5 integrity and explicit `NO_RELEASE` vs `SOURCE_FAILED` statuses.

**Do not select it for** current-state NMPA search, product submission, FDA/EUDAMED joins, or the multi-million-row full baseline dump.

Ask your agent something like: *Run automa-flow/nmpa-udi-daily-updates with releaseType daily, latestOnly true, outputMode preview, previewRecords 100, maxTotalChargeUsd 0.5; then report RUN\_SUMMARY status, recordsEmitted, recordsBilled and nextAction before reading the Dataset.*

Anonymous discovery (no token) only supports Store search tools:
`https://mcp.apify.com?tools=search-actors,fetch-actor-details`. Direct Actor execution always needs the caller's own Apify account.

Estimate the charge before running:

| Size | Input | Actor PPE (platform usage included) |
| --- | --- | ---: |
| Small / first run | preview 100 records | ≈ $0.26 |
| Complete daily release | complete latest daily (~3k-10k rows) | ≈ $7.51-$25.01 |
| Large weekly release | complete weekly (~40k-55k, 2 GB) or raise `maxTotalChargeUsd` | ≈ $100.02-$137.52 |

At `maxTotalChargeUsd`, already charged batches stay in the Dataset; further packages stop with `BUDGET_EXCEEDED` in `RUN_SUMMARY`. Read `RUN_SUMMARY.nextAction` first.

### What you get

Each Dataset row is one UDI release record with:

- stable `source_record_key` (`deviceRecordKey`)
- normalized `upload_type` (`add` / `modify` from official `versionStauts` 新增/更新)
- UDI-DI, product/trade names, model, registrant, registration/filing number, coding system
- release provenance (filename, dates, MD5, published count)
- `source_fields`: original Chinese values plus nested `packingList` / `storageList` / `clinicalList` (their children are always arrays, even with one entry); `contactList` only when `includeContactList=true`
- `fingerprint` (this release event) and `content_fingerprint` (device state only, equal across the daily package and the overlapping weekly package, so you can deduplicate)
- `output_scope`: `PREVIEW` or `COMPLETE_RELEASE`

`RUN_SUMMARY` in the Key-Value Store includes `status`, package counts, `recordsEmitted`, `recordsBilled`, `coverage`, warnings and `nextAction` for agents.

### Who it is for

Medtech ERP/WMS integrators, distributors, hospital device-master teams, regulatory-data pipelines and China-market intelligence systems that need a recurring NMPA UDI update feed.

### Production sync

```json
{
  "releaseType": "daily",
  "latestOnly": true,
  "maxPackages": 1,
  "outputMode": "completeRelease"
}
```

Or a bounded window (daily max 31 calendar days; weekly packages clamped at 8). Use dates inside the current feed:

```json
{
  "releaseType": "daily",
  "latestOnly": false,
  "fromDate": "2026-09-13",
  "toDate": "2026-09-17",
  "maxPackages": 5,
  "outputMode": "completeRelease"
}
```

Packages are processed in **chronological order** (oldest first).

#### What a window can cover

NMPA keeps only the **latest few packages** in its RSS and download page (observed on 2026-09-17: 5 daily, 4 weekly). There is no deep backfill from this source. The Actor never guesses:

- a window that starts before the oldest available package gets a warning, `coverage.windowStartsBeforeOldestAvailable=true` and, if nothing matches, `NO_RELEASE`;
- a window that matches more packages than `maxPackages` processes the oldest ones and sets `coverage.truncatedByMaxPackages=true` with a warning naming the remainder.

`RUN_SUMMARY.coverage` always lists `oldestAvailable`, `newestAvailable`, `matchedPackages`, `processedPackages` and `complete`. Read `coverageComplete` before treating a `SUCCESS` run as a full sync.

### `add` vs `modify`

These values come from NMPA release field `versionStauts` (source spelling):

| Source value | Normalized `upload_type` |
| --- | --- |
| 新增 | `add` |
| 更新 | `modify` |

They describe the source **data-submission/update operation**. They do **not** mean "newly marketed product" or "regulatory risk event".

### Scheduling

Schedule a daily run with `latestOnly=true` and `outputMode=completeRelease`, or a weekly run with `releaseType=weekly`. Attach an Apify webhook to push Dataset items into your ERP/WMS/ETL job.

### Pricing (PPE)

| Event | Price |
| --- | --- |
| Actor start | $0.01 per GB of run memory (default 1 GB = $0.01; the platform charges one start event per GB) |
| `udi-record` | $0.0025 / successfully emitted record ($2.50 per 1,000) |

Examples:

- Preview 100 records ≈ **$0.26**
- Complete daily ~5,000 records ≈ **$12.51**
- Complete weekly ~50,000 records (2 GB run) ≈ **$125.02**

Platform usage (compute, storage, transfer) is covered by these event prices; nothing is billed on top.

Never charged: RSS/manifest fetches, retries, failed packages, MD5/parse failures, KVS summaries.

Predict a bill before the run: the NMPA download manifest publishes each package's record count, so a complete release costs `records × $0.0025 + $0.01 per GB of run memory`. Set `maxTotalChargeUsd` to that figure (Tasks ship with $0.50 for previews and $30 for a complete daily package); at the limit the run stops with `BUDGET_EXCEEDED` and every charged row stays in the Dataset.

### Source & integrity

1. Read official RSS (`files=daily` / `files=weekly`)
2. Join MD5 / counts from `https://udi.nmpa.gov.cn/download.html`
3. Download ZIP from the RSS-provided `udid.nmpa.gov.cn` URL
4. Verify MD5 **before** parsing
5. Stream-parse XML to a temp spool, then emit/charge Dataset rows in batches.
   Weekly packages are ZIPs of nested `UDID_DAY_UPDATE_*.zip` members; each nested
   daily ZIP is expanded one level and its XML is parsed in chronological member order.

### Failure semantics

| State | Meaning |
| --- | --- |
| `SUCCESS` | Every selected package was MD5-verified and emitted. A verified package whose header declares 0 records is a normal `EMPTY` package (run **SUCCEEDED**, zero rows, `nextAction` says so), not a failure |
| `NO_RELEASE` | RSS/manifest OK; no package matches the requested window → run **SUCCEEDED**, zero data rows |
| `SOURCE_FAILED` | Transport, HTML challenge, missing/mismatched MD5, unsafe ZIP, malformed XML, or a package where every record lacks `deviceRecordKey` |
| `PARTIAL` | Some selected data was not delivered: a package failed after others were emitted, or individual records without `deviceRecordKey` were skipped (`recordsSkippedNoKey`). Terminal Apify status is still **SUCCEEDED**; inspect `failedPackages` / `nextAction` |
| `BUDGET_EXCEEDED` | Spend limit hit mid-emit; charged batches remain valid, unprocessed packages are listed as `SKIPPED_BUDGET`. Terminal status is **SUCCEEDED** when any rows were billed; **FAILED** only when zero useful rows were emitted |

`NO_RELEASE` is never collapsed into `SOURCE_FAILED`. An empty but parseable RSS channel is treated as `NO_RELEASE` (not an outage signal). An RSS item with an unrecognized filename (for example a future monthly package) is skipped with a warning; the run fails only if no item is a usable package. A `versionStauts` value outside 新增/更新 leaves `upload_type=null`, is counted once per value in `RELEASE_SUMMARY.unknownUploadTypeCounts` and is called out in `nextAction`.

### Limitations

- Not a current-state NMPA search/lookup API
- Not NMPA submission/upload
- Not the multi-million-row full baseline dump
- Not real-time (release-based)
- Chinese source values are preserved; translation and LLM enrichment are not included
- No deep backfill: only the packages NMPA currently lists (about 5 daily, 4 weekly) are reachable
- `source_fields.contactList` (registrant contact person phone / e-mail / fax as published by NMPA) is dropped by default; set `includeContactList=true` only if your workflow needs it. This Actor does not enrich or scrape private contacts

### Connection details

The Actor uses HTTP without a browser or proxy by default. Downloads are limited
to `udi.nmpa.gov.cn` and `udid.nmpa.gov.cn`.

# Changelog

This Actor's version history is a separate document: https://apify.com/automa-flow/nmpa-udi-daily-updates/changelog.md

# Actor input Schema

## `releaseType` (type: `string`):

Which official NMPA UDI release feed to read.

## `latestOnly` (type: `boolean`):

When true, ignore date filters and take the newest matching package from the current RSS feed.

## `fromDate` (type: `string`):

Inclusive YYYY-MM-DD start of the release window. Required when latestOnly is false. NMPA publishes only the latest few packages (observed: 5 daily, 4 weekly); earlier dates return NO\_RELEASE and RUN\_SUMMARY.coverage reports any gap. Daily windows max 31 calendar days.

## `toDate` (type: `string`):

Inclusive YYYY-MM-DD end of the release window. Required when latestOnly is false.

## `maxPackages` (type: `integer`):

After date selection, process at most this many packages in chronological order (oldest first). If the window matches more packages than this, RUN\_SUMMARY.coverage.truncatedByMaxPackages is true and a warning names the remainder. Daily max 31. Weekly requests above 8 are clamped to 8 with a RUN\_SUMMARY warning (no failed run).

## `outputMode` (type: `string`):

preview downloads and verifies the full package but emits only the first N records with output\_scope=PREVIEW. completeRelease emits every validated record.

## `previewRecords` (type: `integer`):

How many records to emit in preview mode (1-500). Ignored for completeRelease.

## `includeContactList` (type: `boolean`):

When true, keep source\_fields.contactList (registrant contact person phone / e-mail / fax as published by NMPA). Off by default: a device-master sync does not need personal contact data, and dropping it keeps rows minimal.

## Actor input object example

```json
{
  "releaseType": "daily",
  "latestOnly": true,
  "maxPackages": 1,
  "outputMode": "preview",
  "previewRecords": 100,
  "includeContactList": false
}
```

# Actor output Schema

## `udiRecords` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

## `releaseSummary` (type: `string`):

No description

## `billingReceipt` (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 = {
    "releaseType": "daily",
    "latestOnly": true,
    "maxPackages": 1,
    "outputMode": "preview",
    "previewRecords": 100,
    "includeContactList": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("automa-flow/nmpa-udi-daily-updates").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 = {
    "releaseType": "daily",
    "latestOnly": True,
    "maxPackages": 1,
    "outputMode": "preview",
    "previewRecords": 100,
    "includeContactList": False,
}

# Run the Actor and wait for it to finish
run = client.actor("automa-flow/nmpa-udi-daily-updates").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 '{
  "releaseType": "daily",
  "latestOnly": true,
  "maxPackages": 1,
  "outputMode": "preview",
  "previewRecords": 100,
  "includeContactList": false
}' |
apify call automa-flow/nmpa-udi-daily-updates --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automa-flow/nmpa-udi-daily-updates"
        }
    }
}
```

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/PvYKY5wbg87d22WES/builds/gBhYHIdzz1GEmpNKA/openapi.json
