Developer docs
Sandbox · API v2PlaygroundGet API keys

Import jobs

An Import Job ingests many products at once. Row failures are isolated, one bad row does not fail the batch, and you correct and re-submit against a generated template.

Lifecycle

HTTP
POST /api/v2/import/jobs        (multipart: file=CSV, UTF-8, ≤10 MB, magic-byte validated)  → 202 { jobId }
GET  /api/v2/import/jobs/{jobId}                     → status / preview
GET  /api/v2/import/jobs/{jobId}/errors              → row-level failures (isolated)
GET  /api/v2/import/jobs/{jobId}/correction-template → fixable re-upload template
POST /api/v2/import/jobs/{jobId}/confirm             → apply

Get the right template first: GET /api/v2/schemas/categories/{category}/import-template?jurisdiction=EU renders the CSV columns from the category's effective schema, fill it in and upload it via POST /api/v2/import/jobs; the columns round-trip by construction. Also: POST /api/v2/import (single-shot CSV or XLSX, ≤ 10 MB) and GET /api/v2/import/templates/{regulationCode} (same generator, keyed by regulation code). Re-submitting the same file is a no-op.

Malware scan: both upload routes scan every file before anything is parsed, stored or queued, and refuse the upload rather than import it unscanned. 413 FILE_TOO_LARGE_TO_SCAN: the file is larger than 10 MB, the most the scan accepts. 422 MALWARE_FOUND: the scan found harmful content; nothing was imported, and the response carries a reference to quote to support. 503 SCAN_UNAVAILABLE: the scan could not run, so nothing was imported; retry after the Retry-After interval. A request body larger than 12 MB is refused with 413 before it is read, and one that takes longer than 60 seconds to arrive is refused with 408.

Machine on-ramp

The JSON batch path ERP-style integrations use, no multipart, API-key auth:

HTTP
POST /api/v2/ingest/products                    (scope: ingest:write)
{ "sourceSystem": "erp-middleware",
  "products": [ { "referenceId": "erp-4711",    // REQUIRED, per-row idempotency key
                  "name": "EcoCell Battery Pack",  // REQUIRED
                  "description": "…", "gtin": "…", // optional
                  "category": "Electronics",    // optional display-name enum
                  "materials": [{ "name": "lithium", "percentage": 5.2 }],
                  "manufacturing": { "facility": "Gigafactory 1", "country": "DE" },
                  "customData": { "line": "A" } } ] }

→ 202 { jobId, links: { status } }
GET /api/v2/ingest/{jobId}          → job status / row outcomes (poll this)

The item shape is the strict ERP-flavoured ingest schema (unknown keys rejected), NOT the create-product shape. referenceId is the per-row identity: re-submitting the same rows does not duplicate products. Completion is observed by polling the status URL returned with the 202; that is the only completion mechanism for this path, and the import.completed / import.failed webhook events are not emitted for it.

No completion callback (since 2026-09-24). A request that includes callbackUrl is refused 400 CALLBACK_URL_UNSUPPORTED, whatever the URL. The field used to be accepted, and it was never called. Omit it and poll the status URL returned with the 202.
Status. Measured 2026-07-22 (10,000 rows terminal in 67.5 s · 25/25 induced failures isolated row-level · idempotent re-submit, zero duplicates), Implemented. AASX package import: POST /api/v2/import/aasx accepts a multipart .aasx (OPC/ZIP) package and creates a Draft product through the canonical create contract; XML-only payloads are refused honestly.
Was this page helpful?
Thanks, noted.Feedback goes to the docs team by email.