# Medicine Shortage Monitor - Spain, Norway and EMA (`prixodik/medicine-shortage-monitor`) Actor

Complete medicine-shortage reports across Spain, Norway and EMA with package enrichment, ATC links, watchlists, source dates and change tracking.

- **URL**: https://apify.com/prixodik/medicine-shortage-monitor.md
- **Developed by:** [Milchenko Oleksii](https://apify.com/prixodik) (community)
- **Categories:** Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1,940.00 / 1,000 complete medicine shortage reports

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

## Medicine Shortage Monitor - Spain, Norway and EMA

Get a complete, structured medicine-shortage report for international procurement research. Combine official Spanish package shortages, the Norwegian supplier-report table and the EMA shortage catalogue. Enrich Spanish packages with substances, dosage, formulation and exact human ATC level-5 codes. Track a watchlist and changes since the last successful capture.

### Pricing

**$1.99 per complete report: $0.05 per start + $1.94 per delivered report.** Platform usage is included at 512 MB. Each run writes exactly one report row to its default dataset. All normalized records and change files are included; there is no per-medicine or per-file charge. An unchanged report or a watchlist with no matches is still a completed paid check. Failed or incomplete captures do not create a report; the start fee still applies. If a complete report is delivered before a later platform interruption, that delivered report can still be charged.

Examples: one complete run $1.99; weekly checks about $7.96 for four runs; 30 daily checks $59.70. Set a spending limit of at least $1.99. These are standard event prices; Store discounts, if enabled later, will be shown by Apify.

### Quick start

```json
{"sources":["AEMPS","DMP","EMA"],"category":"human","activeOnly":true,"mode":"snapshot","stateStoreName":"my-medicine-portfolio","atcCodes":[],"medicineNames":[],"cacheHours":12,"maxSourceAgeDays":7}
```

Select any non-empty combination of official sources. Category and watchlist filters apply to the selected file; the full captured file preserves source history and categories. `atcCodes` uses exact seven-character human codes, for example `N02BE01`. `medicineNames` performs case-insensitive substring matching on published original names. Name and ATC filters combine using AND. Name matching is not medicine identity verification.

Use `mode: "changes"` and the same state-store name and portfolio filters for a later run. No baseline means no established transitions, rather than all captured history being labelled new shortages. Both modes return a complete report with change information. Changed portfolio filters create an independent baseline.

### Outputs

The default dataset contains one **complete report**, including source counts, selected count, change count, baseline identity, source publication dates and links to included files:

- `RECORDS`: all selected normalized records.
- `FULL_RECORDS`: the complete captured source scope, including historical and veterinary notices where published.
- `CHANGES`: selected source transitions and separately labelled uncertain transitions.
- `SUMMARY`: source freshness, categories, quality issues, requests, restored-cache counts and limitations.

JSON and CSV exports of the report row are available in Apify. Download normalized medicine rows from `RECORDS` or `FULL_RECORDS` through the output links. Private storage links require your Apify authentication.

Normalized records include English machine-readable keys, original medicine names and substance text with `sourceLanguage`, agency attribution, official URLs, source status and normalized status, dates, full ATC codes, identity confidence and explicit unknowns. Original Norwegian fields are represented as a list of `fieldNameOriginal` / `valueOriginal` pairs, so original values and field labels remain available without introducing non-English JSON keys.

### Source scope and meaning

- **AEMPS / Spain**: full captured active-shortage list; details verified against each national package code. The API does not supply a list publication timestamp; reading time is not presented as one.
- **DMP / Norway**: full published supplier-report table, including historical, upcoming and veterinary notices. The published status date is preserved. Notices are not pharmacy stock.
- **EMA**: the EMA-assessed shortage catalogue, not every national European shortage. Medicine enrichment requires one exact linked catalogue medicine with a matching original name and category. Uncertain identity remains unknown.

All selected sources must succeed completely. A stale dated source, missing required identity, duplicate record, inconsistent pagination or substantial count drop fails the report. Invalid non-identity dates are preserved as original text and quality issues; missing dates are never guessed. A count-drop guard is a heuristic and does not prove completeness of smaller changes.

Cross-source links mean **the same confirmed human ATC level-5 classification**, not the same package or product. Veterinary codes retain their category. No clinical equivalence, stock availability, substitution recommendation or import clearance is established. Foreign-package dates are source facts requiring separate applicability checks.

### State, cache and scheduling

Every source list is reread on every run. Spanish details and EMA linkage decisions expire individually after `cacheHours` (0-24, default 12). Use zero for fresh detail reads. Even valid cached EMA links use the freshly read medicine catalogue. Cached data is not presented as freshly fetched data.

State uses immutable per-run checkpoints and delivery markers. A checkpoint qualifies as a subsequent baseline only after its complete files and report row have been read back and verified. Incomplete captures do not advance it. A later platform interruption after complete delivery can leave a valid delivered baseline; this rule does not require permission to inspect earlier runs. Eight completed checkpoints are retained. Overlapping runs can compare the same baseline: schedule non-overlapping runs for one change stream. This is not an exactly-once event service or an atomic shared lock. Corrected Norwegian identifiers are provisional: ambiguous replacements and declaration-date corrections remain uncertain. A missing source row is not declared a resolved shortage.

Cold full captures can take about 9-15 minutes; cached repeats are usually much shorter. Runtime has 512 MB, an 1800-second limit, at most two simultaneous HTTP reads, at most two request starts per second and a bounded request budget. No browser, proxy, third-party paid Actor or AI service is required.

### Attribution and terms

Sources: [AEMPS open data](https://sede.aemps.gob.es/datos-abiertos/) and [legal notice](https://www.aemps.gob.es/aviso-legal/); [European Medicines Agency legal notice](https://www.ema.europa.eu/en/about-us/legal-notice); [Norwegian Medical Products Agency reuse terms](https://www.dmp.no/om-oss/om-dmp.no/vilkar-for-bruk-av-tekst-illustrasjonar-foto-og-film).

Agency names, source URLs and reading dates accompany normalized records. Original source-language values are preserved; no translation is invented. Images, logos, videos and complete PDF publications are not redistributed. Germany and Australia are outside this Actor's scope. Official notices can be incomplete or delayed; verify the official source and applicable regulatory requirements before procurement decisions.

# Actor input Schema

## `sources` (type: `array`):

Every selected source must be captured completely; a failed source fails the report.

## `category` (type: `string`):

Select human medicines, veterinary medicines or all published categories. Full captured files preserve every category.

## `activeOnly` (type: `boolean`):

Applies source status, not inferred stock availability. Full captured history is still provided in FULL_RECORDS.

## `atcCodes` (type: `array`):

Full seven-character codes, for example N02BE01. An empty list selects all codes.

## `medicineNames` (type: `array`):

Case-insensitive substring matching on published original names, not translation or clinical equivalence. Combined with ATC filters using AND.

## `mode` (type: `string`):

Both modes produce one complete paid report. Changes mode includes transitions since the previous completed checkpoint, even if no changes are found.

## `stateStoreName` (type: `string`):

Named Apify key-value store owned by your account. Use a separate name for independent portfolios. Immutable checkpoints avoid shared-pointer overwrites; overlapping runs may compare the same baseline.

## `cacheHours` (type: `integer`):

Zero forces fresh detail/link reads. Live source lists are always reread. Expired cache entries are refreshed individually.

## `maxSourceAgeDays` (type: `integer`):

Old dated sources fail the report. Spain provides no publication timestamp; live reading is disclosed separately.

## Actor input object example

```json
{
  "sources": [
    "AEMPS",
    "DMP",
    "EMA"
  ],
  "category": "human",
  "activeOnly": true,
  "atcCodes": [],
  "medicineNames": [],
  "mode": "snapshot",
  "stateStoreName": "medicine-shortage-monitor-state",
  "cacheHours": 12,
  "maxSourceAgeDays": 7
}
```

# Actor output Schema

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

No description

## `records` (type: `string`):

No description

## `fullRecords` (type: `string`):

No description

## `changes` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("prixodik/medicine-shortage-monitor").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("prixodik/medicine-shortage-monitor").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 '{}' |
apify call prixodik/medicine-shortage-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,prixodik/medicine-shortage-monitor"
        }
    }
}
```

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/N27eFyh1yu4pwAcvC/builds/fUHGP1Vuj6JeYQDqO/openapi.json
