# GTIN / UPC / EAN → Product Markdown Card (`ingenious_quip_bxq/gtin-product-markdown`) Actor

Look up GTIN/UPC/EAN barcodes via UPCitemdb or Barcode Lookup (bring your own key) with Open Food Facts as a keyless food/FMCG fallback. One Markdown product card (+ optional RAG chunks) per barcode. 256 MB. Not-found & failed rows free. No Idealo/Amazon scraping.

- **URL**: https://apify.com/ingenious_quip_bxq/gtin-product-markdown.md
- **Developed by:** [新世紀書僮](https://apify.com/ingenious_quip_bxq) (community)
- **Categories:** E-commerce, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 product cards

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

## GTIN / UPC / EAN → Product Markdown Card

Turn a list of barcodes into structured product records and Markdown cards for catalogs, ERP cleanup, and RAG product stores.

### What you get

- **Dataset** — one row per GTIN: `gtin`, `source` (`upcitemdb` | `barcodelookup` | `openfoodfacts` | `none`), `title`, `brand`, `size`, `category`, `images[]`, `description`, `ingredients`, `nutrition` (OFF), `asin` (only if the barcode API returned it), `url`, `markdown`, `errorClass` / `hint`
- **Key-value store**
  - `REPORT.md` — summary + one card per successful product
  - `PRODUCTS.md` — cards only
  - `CHUNKS.jsonl` — optional ~500–800 character RAG chunks with headings
  - `SUMMARY` / `OUTPUT` — run stats and charged events

### Providers (bring your own key)

| Provider | Auth | Coverage |
|---|---|---|
| **UPCitemdb** | Paid `user_key` in `apiKey` (header + `key_type: 3scale`) | General products |
| **Barcode Lookup** | `key` query param from `apiKey` | General products (+ ASIN when present) |
| **Open Food Facts** | **No key** | Food / FMCG fallback — always labeled `source=openfoodfacts` |

**Not included:** Idealo price scraping, Amazon account / storefront crawling. ASIN is an optional field only when UPCitemdb or Barcode Lookup already return it.

#### How to get a key

You pay the barcode provider directly; this Actor never ships with a built-in key.

**UPCitemdb** (general retail products)

1. Open https://www.upcitemdb.com/api and pick a paid plan (DEV or PRO). Check that page for current quotas and prices.
2. After signup, copy your `user_key` from the account dashboard.
3. Paste it into the secret `apiKey` input and set `provider` to `upcitemdb` or `auto`.
   The Actor sends it as the `user_key` header with `key_type: 3scale` to `/prod/v1/lookup`.
4. Optional: `upcitemdbUserId` overrides the UPCitemdb `user_key` if you want a different key than `apiKey`.

**Barcode Lookup** (general products, sometimes includes an ASIN)

1. Open https://www.barcodelookup.com/api and create an account / choose a plan.
2. Copy the API key shown on your dashboard.
3. Paste it into `apiKey` and set `provider` to `barcodelookup` or `auto`.
   The Actor sends it as the `key` query parameter to `/v3/products`.

**Open Food Facts fallback (no key)**

- Leave `fallbackOpenFoodFacts: true` (the default). Barcodes that the paid provider misses, or every barcode when no key is set, are looked up in the free, community-run [Open Food Facts](https://world.openfoodfacts.org) database.
- Coverage is mainly **food, drinks and other FMCG**. Electronics, apparel and similar products usually return `not_found` (free).
- Rows from this source are always labeled `source=openfoodfacts`. Data is under the Open Database License (ODbL); keep attribution when you republish it.
- To try the Actor without any key, run the first example input below with a food barcode.

**Common key errors** (all free rows, with a `hint`)

| `errorClass` | Usual cause | Fix |
|---|---|---|
| `missing_key` | `provider` is `upcitemdb` / `barcodelookup` but `apiKey` is empty, and Open Food Facts did not match | Add your key, or use `provider: auto` with the fallback on |
| `unauthorized` | Wrong, expired or wrong-provider key (HTTP 401/403) | Re-copy the key; make sure it belongs to the provider you selected. With `stopOnAuthError` the run stops calling that provider |
| `rate_limited` | Plan quota or per-minute limit hit (HTTP 429) after retries | Lower `maxConcurrency`, raise your plan, or re-run later |

Never put real keys in Actor environment variables, README examples or shared inputs. Use the secret `apiKey` input (placeholder in examples: `YOUR_KEY`).

### Input highlights

| Field | Default | Notes |
|---|---|---|
| `gtins` / `barcodes` | — | String list; digits only after normalize (strip spaces/dashes) |
| `datasetId` / `keyValueStoreId` | — | Optional bulk sources |
| `provider` | `auto` | `auto` | `upcitemdb` | `barcodelookup` |
| `apiKey` | — | Secret. Required for paid providers |
| `fallbackOpenFoodFacts` | `true` | Keyless food/FMCG fallback |
| `includeRagChunks` | `true` | Write `CHUNKS.jsonl` |
| `maxItems` | `100` | Cap |
| `maxConcurrency` | `2` | Max 5 |
| `maxRetries` | `3` | 429 / 5xx / timeouts |
| `stopOnAuthError` | `true` | Skip further paid calls after 401/403 |

#### `provider=auto` behavior

1. If `apiKey` is set → try **UPCitemdb**, then **Barcode Lookup** on auth failure.
2. On miss (or no key) → **Open Food Facts** when `fallbackOpenFoodFacts` is true.
3. Still missing → `source=none`, `errorClass=not_found` (free).

### Pricing (pay-per-event)

| Event | Price | When |
|---|---|---|
| `apify-actor-start` | $0.001 | Once per run |
| `product-card` | **$0.004** | Each successful product card (any source, including Open Food Facts) |

**Free:** not found, missing key (when OFF also misses / OFF disabled), unauthorized, rate-limited after retries, invalid input, timeouts, network/server errors.

### Example input

```json
{
  "gtins": ["3017620422003"],
  "provider": "auto",
  "fallbackOpenFoodFacts": true,
  "includeRagChunks": true,
  "maxConcurrency": 2
}
```

With a paid key:

```json
{
  "gtins": ["0885909950805", "3017620422003"],
  "provider": "auto",
  "apiKey": "YOUR_KEY",
  "fallbackOpenFoodFacts": true
}
```

### Memory & limits

Default **256 MB** (max 512 MB). Timeout 3600 s. Categories: ECOMMERCE, DEVELOPER_TOOLS, AUTOMATION.

### Attribution

Product data belongs to the upstream provider named in `source`. This Actor only calls official HTTP APIs.

# Changelog

This Actor's version history is a separate document: https://apify.com/ingenious_quip_bxq/gtin-product-markdown/changelog.md

# Actor input Schema

## `gtins` (type: `array`):

One barcode per line (8–14 digits). Spaces and dashes are stripped; non-digit characters are removed. Duplicates are dropped.

## `barcodes` (type: `array`):

Optional alias of `gtins`. Same normalization rules.

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

Optional. Read barcodes from another Actor's dataset. Uses the field set in 'Barcode field', else gtin / upc / ean / barcode / code.

## `keyValueStoreId` (type: `string`):

Optional. Read barcodes from a key-value record: JSON array, {"gtins": \[...]} / {"barcodes": \[...]}, or plain text with one barcode per line.

## `keyValueRecordKey` (type: `string`):

Record key inside the key-value store (default INPUT_GTINS).

## `barcodeField` (type: `string`):

Optional. Name of the field holding the barcode in dataset items or JSON objects.

## `provider` (type: `string`):

Which barcode API to call. `auto` uses your key with UPCitemdb first (then Barcode Lookup on auth failure), then Open Food Facts on miss when fallback is on. Without a key, `auto` goes straight to Open Food Facts.

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

UPCitemdb paid `user_key` or Barcode Lookup `key`. Stored encrypted and never logged. Without a key, paid providers are skipped; Open Food Facts still works when fallback is enabled. Placeholders like YOUR_KEY are ignored.

## `upcitemdbUserId` (type: `string`):

Rarely needed. UPCitemdb paid plans authenticate with the `user_key` header (same value as apiKey) plus fixed `key_type: 3scale`. Use this only if you want a separate user_key distinct from apiKey.

## `fallbackOpenFoodFacts` (type: `boolean`):

When the paid provider misses (or no key is provided under auto), look up the GTIN on Open Food Facts (keyless, food/FMCG coverage). Always labels `source=openfoodfacts` on success.

## `includeRagChunks` (type: `boolean`):

Split each product Markdown card into ~500–800 character chunks with headings. Written to key-value record CHUNKS.jsonl.

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

Stop after this many barcodes. 0 = no limit (still subject to platform limits).

## `maxConcurrency` (type: `integer`):

Parallel provider requests. Keep low to respect provider rate limits.

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

Retries for 429, 5xx and timeouts with exponential backoff (honours Retry-After). 401/403/not-found are not retried.

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

Timeout per provider request.

## `stopOnAuthError` (type: `boolean`):

After a paid provider rejects the key (401/403), mark remaining paid lookups as skipped (still try Open Food Facts when fallback is on). All free.

## Actor input object example

```json
{
  "gtins": [
    "3017620422003",
    "0885909950805"
  ],
  "keyValueRecordKey": "INPUT_GTINS",
  "provider": "auto",
  "fallbackOpenFoodFacts": true,
  "includeRagChunks": true,
  "maxItems": 100,
  "maxConcurrency": 2,
  "maxRetries": 3,
  "requestTimeoutSecs": 30,
  "stopOnAuthError": true
}
```

# Actor output Schema

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

Dataset rows: gtin, source, title, brand, size, category, images, description, ingredients, nutrition, asin, url, errorClass, hint.

## `reportMarkdown` (type: `string`):

Summary counts + one product card per successful GTIN.

## `productsMarkdown` (type: `string`):

Concatenated product cards only (no summary table).

## `chunks` (type: `string`):

Newline-delimited JSON chunks (~500–800 chars) when includeRagChunks is true.

## `summary` (type: `string`):

ok, failed, bySource, byClass, charged, durationSecs, peakMemoryMb.

# 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 = {
    "gtins": [
        "3017620422003",
        "0885909950805"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ingenious_quip_bxq/gtin-product-markdown").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 = { "gtins": [
        "3017620422003",
        "0885909950805",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("ingenious_quip_bxq/gtin-product-markdown").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 '{
  "gtins": [
    "3017620422003",
    "0885909950805"
  ]
}' |
apify call ingenious_quip_bxq/gtin-product-markdown --silent --output-dataset

```

## MCP server setup

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

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/uyChqBXjt25224ocg/builds/a7jHEizO7EoxF0GBA/openapi.json
