# Product Recall Checker: Catalogue vs CPSC & EU Safety Gate (`subtleasianmerch/product-recall-matcher`) Actor

Check your product catalogue for recalls against US CPSC and EU Safety Gate in one run. Matches by barcode, brand and model, with the evidence for each and clear "no recall found" rows. Schedule it to get only new recalls.

- **URL**: https://apify.com/subtleasianmerch/product-recall-matcher.md
- **Developed by:** [Public Data Tools](https://apify.com/subtleasianmerch) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 item checkeds

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?

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

## Product Recall Checker: Catalogue vs CPSC & EU Safety Gate

Check your product catalogue for recalls: every item is matched against **US CPSC recalls** and **EU Safety Gate** alerts in one run. Sellers, marketplaces and importers get likely matches with the evidence, or a clear **"no recall found"**.

- 🔎 **An answer for every item:** likely match, possible match or "no recall found".
- 🇺🇸🇪🇺 **Both markets, one run:** about 1,300 CPSC recalls and 12,500 Safety Gate alerts from 3 years.
- 🧾 **Evidence with every match:** the barcode, brand and model that agreed, and the recall text.
- 📊 **Fewer false alarms:** of 60 ordinary products, brand search flagged 24; this Actor, none as likely.
- 🔔 **New-recall alerts:** scheduled runs return only items with new matches; unchanged items are free.
- 💸 **$2 per 1,000 items checked.** Items that couldn't be fully checked are free.

**Try it:** the input comes pre-filled with three products: a recalled ceiling fan (a CPSC match by brand and model), a recalled plush toy (a Safety Gate match by barcode) and an ordinary mouse (no recall found). Click **Start**, then replace them with your own.

> **Not legal or compliance advice.** This Actor does automated matching against public recall data.
>
> - A *likely* match is strong evidence; still read the recall notice before acting.
> - A *possible* match shares only the brand and part of the name.
> - *No recall found* means nothing matched in the sources and dates checked. It doesn't mean the product is safe or compliant.

### What you get

One row per catalogue item, with your own fields (id, barcode, brand, model, SKU, name, category, country) and one result:

| `result` | What it means | Charged |
|---|---|---|
| `likely-match` | the same barcode, or the same brand and model number, as a recall | yes |
| `possible-match` | the same brand and a similar product name; not a confirmed recall | yes |
| `no-recall-found` | nothing matched in the sources and dates checked | yes |
| `not-checked` | nothing to match on, or a source couldn't be read; the reason is given | free |

**Every row also has:**

- a one-line `summary`, the highest confidence, and the number of likely and possible matches;
- `matches`, up to 10 by default, likely first. Each match has:
  - the source, recall ID and date, title, product name and description;
  - brand, companies, the models and barcodes listed, and batch;
  - hazard and risk level, remedy or measures, units;
  - notifying country, country of origin, and whether the alert is about counterfeits;
  - the recall's link and image links;
  - `matchType`, `confidence`, `label` (likely or possible), a plain-English `why`, and the `evidence`;
- the sources checked or failed, and the recall window (`recallsFrom`, `recallsTo`);
- with a monitor ID: `isNewItem`, each match's `isNew`, and the matches reported in earlier runs;
- each source's licence, licence link and credit line, and a disclaimer.

**The sources:**

| Source | What | How it's read | Dates |
|---|---|---|---|
| **US CPSC** recalls | About 300–500 recalls a year of consumer products sold in the US, with hazards, remedies, firms, and UPCs for some | CPSC's Recall Retrieval Web Services, one request per run | the recall date |
| **EU Safety Gate** alerts | About 4,000 alerts a year from EU and EEA authorities, with brand, model, barcode, batch, risk, measures and notifying country | The Commission's weekly-report XML exports (the "Weekly Reports - XML format" open-data distribution), one file per week | the weekly report's publication date |

### Who it's for

- **Marketplace sellers and resellers** (Amazon, eBay, Etsy, Shopify, second-hand): check stock before listing, and watch for new recalls on what you sell.
- **Marketplaces and retailers:** screen sellers' listings or your own range, by barcode first.
- **Importers and brands:** see whether products like yours, or carrying your barcodes, appear in recalls. Safety Gate often lists counterfeits that carry real barcodes.
- **Compliance and product-safety teams:** a repeatable, logged check with evidence and sources on every row.

### How much does it cost?

You pay **$2 per 1,000 items checked** ($0.002 each), plus Apify's standard run start fee. One event per item, however many matches it has. "No recall found" counts as checked: it's the answer you came for.

| Example | Items | Cost |
|---|---|---|
| The pre-filled example | 3 | $0.006 |
| A test catalogue of 155 rows (2 November 2026): 151 checked, 4 free | 151 | about $0.30 |
| Any 1,000-item catalogue | 1,000 | $2 |

**Free:**

- rows without anything to match on (a brand alone, or a name without a brand), with the reason;
- items whose sources couldn't be read, or could be read only in part;
- with a monitor ID, items with no new recall match since the last run;
- items beyond your spending limit; they come in the next run.

Set a maximum cost per run in the run options. The run stops cleanly at that amount.

### How to use it

1. **Give your catalogue:** a JSON list of items, a CSV or JSON file link (a Shopify or WooCommerce export, or a Google Sheet published as CSV), or an Apify dataset. Common columns are recognised: EAN, UPC, Barcode, Brand, Vendor, Model, MPN, SKU, Title, Country. Map unusual ones in **Column names**.
2. **Choose where you sell:** US (CPSC), EU (Safety Gate) or both. A row's own country wins: US, EU, any EU or EEA country, or both.
3. **Set the window and match level:** recalls from the last 3 years by default, up to 20. Turn possible matches off to get only barcode and model-number matches.
4. **For alerts,** add a **Monitor ID** and schedule the Actor, e.g. weekly. Keep **Output only new items and new matches** on.
5. **Click Start.** The **Recall check** table shows each item's result, confidence, match counts and summary. Export it as JSON, CSV or Excel.

The `RUN_SUMMARY` record in the run's key-value store has the totals:

- how many recalls each source had in the window;
- how many Safety Gate weekly reports were downloaded or reused;
- which columns were used from each part of your catalogue.

### Example input

**A Shopify product export, checked against both sources:**

```json
{
  "catalogueUrl": "https://example.com/exports/products.csv",
  "defaultCountry": "both"
}
```

**EU-only, barcodes and model numbers only, the last 5 years:**

```json
{
  "items": [
    { "id": "A-1001", "gtin": "8056379188698", "brand": "Giochi Preziosi", "model": "PUF39000", "name": "Smurfs plush 20 cm" },
    { "id": "A-1002", "gtin": "5099206080263", "brand": "Logitech", "model": "G502 HERO", "name": "Gaming mouse" }
  ],
  "defaultCountry": "EU",
  "recallYears": 5,
  "includePossibleMatches": false
}
```

**Weekly new-recall alerts for a catalogue kept in a dataset (schedule it once a week):**

```json
{
  "datasetId": "my-shop-catalogue",
  "monitorId": "my-shop"
}
```

### Example output

One row per catalogue item. The pre-filled ceiling fan, a likely match, shortened:

```json
{
  "itemId": "fan-01",
  "brand": "Hampton Bay",
  "model": "AK396H-MBK",
  "name": "Hampton Bay Halwin 52 in. LED Indoor/Outdoor Ceiling Fan, Matte Black",
  "country": "US",
  "result": "likely-match",
  "summary": "1 likely recall match. Best: CPSC-26702 (2026-08-13), same brand and model number.",
  "bestConfidence": "high",
  "likelyMatches": 1,
  "possibleMatches": 0,
  "matches": [
    {
      "source": "CPSC",
      "recallId": "CPSC-26702",
      "recallDate": "2026-08-13",
      "title": "Hampton Bay Halwin 52-Inch Ceiling Fans Recalled Due to Impact and Injury Hazards; Manufactured by Youngo Limited",
      "barcodesListed": "840059614922, 840059615370",
      "hazard": "The fan blades can separate from the fan motor flywheel, posing impact and injury hazards to consumers.",
      "remedy": "Consumers should stop using the recalled ceiling fans immediately and contact Youngo … (Refund)",
      "units": "About 9,460",
      "url": "https://www.cpsc.gov/Recalls/2026/Hampton-Bay-Halwin-52-Inch-Ceiling-Fans-Recalled-Due-to-Impact-and-Injury-Hazards-Manufactured-by-Youngo-Limited",
      "matchType": "brand+model",
      "confidence": "high",
      "label": "likely",
      "why": "Likely match: same brand and model number.",
      "evidence": {
        "brand": { "catalogue": "Hampton Bay", "foundIn": "title or product name" },
        "model": { "kind": "exact", "catalogue": "AK396H-MBK", "recall": "AK396H-MBK", "where": "recall text",
                   "context": "…Ceiling fans with model numbers AK396H-MBK and AK396H-BN. The recalled fans have an adjustable white LED light,…" }
      }
    }
  ],
  "sourcesChecked": ["CPSC"],
  "checkComplete": true,
  "charged": true,
  "recallsFrom": "2023-11-03",
  "recallsTo": "2026-11-03",
  "licence": "US CPSC recalls: CPSC public information (US government work); credit CPSC; no CPSC endorsement implied; images linked, not copied",
  "attribution": "Source: U.S. Consumer Product Safety Commission (CPSC) Recalls database, saferproducts.gov Recall Retrieval Web Services, retrieved 2026-11-03. CPSC does not endorse this service."
}
```

A "no recall found" row has `result: "no-recall-found"`, an empty `matches` list, and the sources and dates checked.

#### How matching works

| Evidence | Match type | Confidence | Label |
|---|---|---|---|
| Same barcode (UPC-A, EAN-13 and GTIN-14 compared as one number) | `gtin` | high | likely |
| Same brand and the same model number (case, spaces and dashes ignored) | `brand+model` | high (medium for generic part codes such as CR2032 or GU10) | likely |
| Same brand; your model is a variant of the recalled one (`MWMS07-BLK` for `MWMS07`) | `brand+model-variant` | medium | likely |
| A distinctive model number alone (5+ characters with letters and digits), no brand to compare | `model` | medium | likely |
| The same model number, but Safety Gate names a different brand | `model-brand-differs` | low | possible |
| Same brand, most of the product name's words | `brand+name` | low | possible |
| A brand alone | – | – | never a match |

- **CPSC model numbers and UPCs** are read from the recall text, because CPSC publishes no model field.
- **Exclusions are respected:** when a recall says a model is **not** included ("if model number is EFMIS129-B … it is not included in this recall"), that model isn't matched to it.

### Integrations

- **Schedules:** run it weekly in Apify Console. With a monitor ID, each run returns only items with new recall matches.
- **Apify API:** check items from your own system and get the rows back as JSON:

  ```bash
  curl -X POST "https://api.apify.com/v2/acts/subtleasianmerch~product-recall-matcher/run-sync-get-dataset-items?token=YOUR_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"items": [{"id": "sku-1", "gtin": "0840059614922", "brand": "Hampton Bay", "name": "Halwin ceiling fan"}], "defaultCountry": "US"}'
  ```

  Replace `YOUR_TOKEN` with your Apify API token. A first call that needs Safety Gate takes a few minutes; use the asynchronous run endpoint for big catalogues.
- **Webhooks:** call your own endpoint when a run finishes, for example to open a ticket for each likely match.
- **Make, Zapier and n8n:** send new recall matches to Slack, email or your product system with Apify's apps for these tools.
- **Monitoring:** Apify's monitoring can alert you when a scheduled run fails.

### FAQ

#### Is it legal to use?

The recall data is public, and both sources' terms, read on 3 November 2026, allow reuse with credit:

- **CPSC:** its notice says its web text is public information that may be freely copied or linked, with credit to CPSC, and not in a way that implies CPSC endorsement. Recall images may be licensed from stock services, so they are linked, not copied.
- **EU Safety Gate:** reuse is authorised if the source is acknowledged and the alerts' meaning isn't distorted (Commission Decision 2011/833/EU). data.europa.eu lists the weekly-report XML under CC0 1.0.

Every row carries the credit lines. Recalls name firms and their business contacts, as the agencies publish them. The Actor's results are automated matching, not legal or compliance advice.

#### Is a "likely match" a confirmed recall?

No. It means the barcode, or the brand and model number, are the same as in the recall. Read the recall notice (the `url`) and check batch, serial and date details before acting.

#### Why is "no recall found" charged?

Because it's the answer you came for: the item was checked against every source it needed. If a source couldn't be read, the item is free and says so.

#### Which recalls are covered?

- **US:** CPSC consumer-product recalls. Food, drugs, cosmetics, cars and tyres are FDA, USDA and NHTSA matters, and aren't included.
- **EU:** Safety Gate alerts for non-food products, including cosmetics and some vehicles. Food alerts (RASFF) aren't included.

#### Why does the first run take longer?

Safety Gate is read one weekly report at a time: about 156 files for three years, 2–4 minutes. They're kept in a key-value store named `product-recall-matcher-cache` in your account, so later runs read only the newest reports and take well under a minute. Turn **Reuse saved Safety Gate weekly reports** off to read everything each time.

#### Does it copy recall photos?

No. It gives links to the images on CPSC's and the Commission's sites. CPSC says some images may be licensed from stock services.

#### What does the Actor store in my account?

- With a monitor ID: a key-value store named `product-recall-matcher-monitor`, holding each item's identity and the recall IDs already reported.
- With the cache on (the default): `product-recall-matcher-cache`, holding Safety Gate weekly reports already read.

You can delete either at any time.

#### Can it check other countries?

Not yet. Health Canada, the UK's product recalls, Australia's ACCC and France's RappelConso are possible additions.

### Accuracy

The matching was tested before building, on 215 catalogue rows:

- recalled products, written the way catalogues write them;
- ordinary products from Open Products Facts and Open Beauty Facts;
- a mixed list;
- a held-out list, made after tuning.

On the held-out list (24 recalled and 40 ordinary products), against a plain brand-name search of the same three years of recalls:

| | Recalled items found | Items wrongly flagged |
|---|---|---|
| This Actor, likely matches only | 17 of 24 | 4 |
| This Actor, with possible matches | 21 of 24 | 5 |
| Brand-name search | 20 of 24 | 14 |

Three of the Actor's false flags were coin-cell sizes (CR2025, CR2430) taken for model numbers; that's fixed. Another was a Safety Gate alert about counterfeits carrying the item's real barcode. On a list of 60 ordinary products, mostly from well-known brands, a brand search flagged 24; this Actor flagged none as likely.

**Known limits:**

- **Recalls tied to serial numbers or dates:** some recalls cover only certain serial numbers or production dates. The match can't see those; the recall text, given in each match, says which.
- **Items with no barcode, model or brand:** these can't be matched.
- **CPSC model numbers:** they come from the recall text, which usually lists them but not always in the same way.
- **Counterfeits:** Safety Gate sometimes lists counterfeits carrying the real product's barcode. Such matches are marked `counterfeit: true`; your genuine stock may not be affected.

### Data sources and credits

- **US Consumer Product Safety Commission:** Recalls database, Recall Retrieval Web Services at saferproducts.gov.

  - CPSC's notice: its web text is public information that may be freely copied or linked, with credit to CPSC, and not in a way that implies CPSC endorsement;
  - recall images may be licensed from stock services, so they are linked, not copied.

  CPSC does not endorse this Actor.
- **EU Safety Gate:** "Alerts from the Rapid Alert System for dangerous non-food products, published free of charge on the Safety Gate website (https://ec.europa.eu/safety-gate-alerts) © European Union, 2005 - 2026."
  - The English version is read; the extraction date is on every row.
  - Reuse follows Commission Decision 2011/833/EU. data.europa.eu lists the weekly-report XML under CC0 1.0.
  - The European Commission does not take responsibility for the accuracy of the alerts, which come from national authorities.

# Actor input Schema

## `items` (type: `array`):

Your products as a JSON array. Each can have gtin (or upc, ean, barcode), brand, model (or mpn), sku, name (or title), category, country (US, EU or both) and id. The more you give, the better: a barcode or a brand with a model number gives likely matches; a brand with a name gives possible matches only.

## `catalogueUrl` (type: `string`):

A link to a CSV or JSON file of your products, e.g. a shop export or a Google Sheet published to the web as CSV. Column names like EAN, UPC, Barcode, Brand, Manufacturer, Model, MPN, SKU, Title and Country are recognised. Up to 50 MB.

## `datasetId` (type: `string`):

The ID or name of an Apify dataset whose items are your products, e.g. the output of a shop scraper or of Google Sheets Sync's import.

## `fieldMapping` (type: `object`):

Only if your columns have unusual names: which column holds which field, e.g. {"gtin": "Code EAN", "brand": "Marque", "model": "Ref fabricant", "name": "Libellé"}. Fields: id, gtin, brand, model, mpn, sku, name, category, country.

## `defaultCountry` (type: `string`):

Which recalls to check for items whose row doesn't say: US (CPSC), EU (Safety Gate) or both. A row's own country (US, EU, an EU or EEA country, or both) wins.

## `recallYears` (type: `integer`):

How far back to look. CPSC is read in one request whatever the window; Safety Gate is read one weekly report at a time, so a first run with a long window takes longer (about 1 minute per year), and later runs reuse the saved reports.

## `includePossibleMatches` (type: `boolean`):

On: an item whose brand and most of its name appear in a recall gets it as a "possible" match, never called a confirmed recall. Off: only likely matches (barcode, or brand and model number).

## `maxMatchesPerItem` (type: `integer`):

Likely matches come first, then at most 5 possible ones, closest name first. The row says how many more there were.

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

Leave empty for up to 50,000 rows a run. Each item checked against all its sources is charged once.

## `monitorId` (type: `string`):

Any name, e.g. "my-shop". Runs with the same ID remember the recalls already reported for each item, so a schedule delivers only new items and items with new recall matches.

## `onlyChanges` (type: `boolean`):

With a monitor ID: on, items with no new recall match since the last run are left out, free; an item with new matches lists only those. Off, every item is output and charged, with new matches marked.

## `useCache` (type: `boolean`):

On: weekly reports already read are kept in a key-value store named product-recall-matcher-cache in your account, so later runs read only new reports. Reports from the last 4 weeks are always read again.

## Actor input object example

```json
{
  "items": [
    {
      "id": "fan-01",
      "brand": "Hampton Bay",
      "model": "AK396H-MBK",
      "name": "Hampton Bay Halwin 52 in. LED Indoor/Outdoor Ceiling Fan, Matte Black",
      "country": "US"
    },
    {
      "id": "toy-07",
      "gtin": "8056379188698",
      "brand": "Giochi Preziosi",
      "name": "Smurfs plush toy 20 cm",
      "country": "EU"
    },
    {
      "id": "mouse-03",
      "gtin": "5099206080263",
      "brand": "Logitech",
      "model": "G502 HERO",
      "name": "Logitech G502 HERO Gaming Mouse",
      "country": "both"
    }
  ],
  "defaultCountry": "both",
  "recallYears": 3,
  "includePossibleMatches": true,
  "maxMatchesPerItem": 10,
  "onlyChanges": true,
  "useCache": true
}
```

# Actor output Schema

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

The dataset rows this run produced.

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

Counts, costs and anything skipped, saved as RUN_SUMMARY.

# 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 = {
    "items": [
        {
            "id": "fan-01",
            "brand": "Hampton Bay",
            "model": "AK396H-MBK",
            "name": "Hampton Bay Halwin 52 in. LED Indoor/Outdoor Ceiling Fan, Matte Black",
            "country": "US"
        },
        {
            "id": "toy-07",
            "gtin": "8056379188698",
            "brand": "Giochi Preziosi",
            "name": "Smurfs plush toy 20 cm",
            "country": "EU"
        },
        {
            "id": "mouse-03",
            "gtin": "5099206080263",
            "brand": "Logitech",
            "model": "G502 HERO",
            "name": "Logitech G502 HERO Gaming Mouse",
            "country": "both"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("subtleasianmerch/product-recall-matcher").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 = { "items": [
        {
            "id": "fan-01",
            "brand": "Hampton Bay",
            "model": "AK396H-MBK",
            "name": "Hampton Bay Halwin 52 in. LED Indoor/Outdoor Ceiling Fan, Matte Black",
            "country": "US",
        },
        {
            "id": "toy-07",
            "gtin": "8056379188698",
            "brand": "Giochi Preziosi",
            "name": "Smurfs plush toy 20 cm",
            "country": "EU",
        },
        {
            "id": "mouse-03",
            "gtin": "5099206080263",
            "brand": "Logitech",
            "model": "G502 HERO",
            "name": "Logitech G502 HERO Gaming Mouse",
            "country": "both",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("subtleasianmerch/product-recall-matcher").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 '{
  "items": [
    {
      "id": "fan-01",
      "brand": "Hampton Bay",
      "model": "AK396H-MBK",
      "name": "Hampton Bay Halwin 52 in. LED Indoor/Outdoor Ceiling Fan, Matte Black",
      "country": "US"
    },
    {
      "id": "toy-07",
      "gtin": "8056379188698",
      "brand": "Giochi Preziosi",
      "name": "Smurfs plush toy 20 cm",
      "country": "EU"
    },
    {
      "id": "mouse-03",
      "gtin": "5099206080263",
      "brand": "Logitech",
      "model": "G502 HERO",
      "name": "Logitech G502 HERO Gaming Mouse",
      "country": "both"
    }
  ]
}' |
apify call subtleasianmerch/product-recall-matcher --silent --output-dataset

```

## MCP server setup

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

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/2GqBUUihqaRyhEbc1/builds/Qx86TZNTTkxCfSVQA/openapi.json
