Errors
The Pixee PIM API uses standard HTTP status codes and returns structured JSON error responses when something goes wrong.
Error response format
Most error responses follow this unified envelope:
Error response
{
"error": {
"code": 404,
"error_code": "PRODUCT_NOT_FOUND",
"type": "not_found",
"message": "The EAN field is required.",
"details": null,
"path": "/api/v1/products/550e8400-e29b-41d4-a716-446655440000",
"request_id": "abc-123",
"timestamp": "2026-09-17T10:30:00Z"
}
}
| Field | Description |
|---|---|
error.code | HTTP status code, repeated in the body |
error.error_code | Standardized machine-readable code (e.g. PRODUCT_NOT_FOUND) |
error.type | Error category: validation_error, not_found, conflict, authentication_error, authorization_error, database_error, rate_limit_exceeded, internal_server_error, http_error |
error.message | Human-readable, localized description (see Localization below) |
error.details | Optional structured detail — e.g. a list of field errors on 422 |
error.path | The request path that produced the error |
error.request_id | Correlates with server-side logs — include it when reporting an issue |
error.timestamp | ISO 8601 timestamp of when the error occurred |
For 5xx errors, message is intentionally a generic, safe string (tied to request_id) — the original exception detail is never leaked to the client, only to server-side logs. For 4xx errors, message carries real, actionable information (e.g. "this SKU already exists").
Divergent shapes — endpoints that don't use the envelope above
A handful of error sources predate the unified envelope and were deliberately not rewritten to avoid silently changing an already-integrated contract. Match on the field names below, not on error.code:
| Situation | Status | Shape |
|---|---|---|
| Rate limit exceeded | 429 | { "error": "rate_limit_exceeded", "message", "detail", "retry_after" } — error is a string here. See Rate Limiting. |
| CSRF token missing/invalid | 403 | { "detail", "error_type": "csrf_validation_error", "debug": {...} } |
| Module disabled on this instance | 403 | { "detail", "code": "MODULE_DISABLED", "module_id": "..." } |
| Instance license suspended (read-only mode) | 403 | { "detail", "code": "LICENSE_SUSPENDED" } |
HTTP status codes
| Status | Meaning |
|---|---|
200 OK | Request succeeded |
201 Created | Resource created successfully |
202 Accepted | Request accepted — async processing started |
204 No Content | Request succeeded, no body returned |
400 Bad Request | Invalid request parameters |
401 Unauthorized | Missing, invalid, or expired credential |
403 Forbidden | Insufficient scope or permissions — or MODULE_DISABLED / LICENSE_SUSPENDED (see above) |
404 Not Found | Resource not found |
409 Conflict | Resource already exists (e.g. duplicate EAN) — or a dependency this operation needs is not configured on the instance (see Dependency not configured) — or the plan's product cap could not be checked right now (BILLING_LICENSE_QUOTA_BUSY, retry) |
422 Unprocessable Entity | Validation error on request body |
423 Locked | Account locked after repeated failed login attempts |
429 Too Many Requests | Rate limit exceeded — see Rate Limiting |
500 Internal Server Error | Unexpected server error |
503 Service Unavailable | A configured dependency is momentarily unreachable, or maintenance. Always carries a Retry-After header and error_code: SYSTEM_SERVICE_UNAVAILABLE — retry after the delay. Since September 18, 2026 a missing configuration is never a 503 |
Dependency not configured (409)
Some operations depend on something an administrator sets up per instance: an Odoo connection, a connector's webhook secret, an AI provider assigned to a usage, the shared key of the price monitoring service, the optional vector search engine. When that setup is missing, the API answers 409 Conflict with an error_code from the *_NOT_CONFIGURED family. This is a permanent state of the instance, not an outage: retrying will not help, fixing the configuration will. error.details names the dependency and the fix.
409 — dependency not configured
{
"error": {
"code": 409,
"error_code": "ODOO_NOT_CONFIGURED",
"type": "conflict",
"message": "No Odoo connection is configured on this instance. Enter the Odoo URL, database and credentials in the Odoo Sync screen before running this import.",
"details": { "dependency": "odoo", "how_to_fix": "Renseignez l'URL, la base et les identifiants Odoo dans l'écran Odoo Sync (/odoo-sync)" },
"path": "/api/v1/brands/import-from-odoo",
"request_id": "abc-123",
"timestamp": "2026-09-18T10:30:00Z"
}
}
| Error code | Raised by | What to do |
|---|---|---|
ODOO_NOT_CONFIGURED | POST /brands/import-from-odoo | An administrator enters the Odoo URL, database and credentials in the Odoo Sync screen |
CONNECTOR_WEBHOOK_NOT_CONFIGURED | POST /connectors/{shopify,woocommerce,bigcommerce}/webhook (409), POST /connectors/magento/push (401) | The connector or its webhook secret is missing on the PixeePIM side — the sender's credentials are not at fault |
AI_PROVIDER_NOT_CONFIGURED | POST /enrichment/products/{product_id} | Add a provider key in Settings → AI Services, or assign a provider to the “enrichment” usage |
SCRAPER_SYNC_KEY_NOT_CONFIGURED | GET /prices/export-watchlist, POST /prices/observations | The shared key with the price monitoring service is not set on this instance — ask an administrator |
SEARCH_VECTOR_NOT_CONFIGURED | GET /search/products/{product_id}/similar | Similarity search is an optional add-on; ask an administrator to enable it on the instance |
How to tell the two apart. 409 + *_NOT_CONFIGURED: the dependency was never set up — surface the message to whoever administers the instance and do not retry automatically. 503 + SYSTEM_SERVICE_UNAVAILABLE: the dependency is configured but unreachable right now — honour Retry-After and retry. Before September 18, 2026 both cases answered 503; integrations that treated those as transient outages should update their handling.
Validation errors (422)
Field-level validation failures populate error.details as an array:
422 Response
{
"error": {
"code": 422,
"error_code": "VALIDATION_FAILED",
"type": "validation_error",
"message": "Request validation failed",
"details": [
{ "field": "body.name", "message": "Field required", "type": "missing" }
],
"path": "/api/v1/products",
"request_id": "abc-123",
"timestamp": "2026-09-17T10:30:00Z"
}
}
Common error codes
| Error code | Description |
|---|---|
AUTHENTICATION_REQUIRED | No credential provided |
INVALID_API_KEY | API key is invalid or revoked |
PERMISSION_DENIED | Credential does not have access to this resource |
NOT_FOUND / PRODUCT_NOT_FOUND / IMPORT_JOB_NOT_FOUND | The requested resource does not exist |
VALIDATION_FAILED | One or more fields failed validation |
DUPLICATE_EAN | A product with this EAN already exists |
RATE_LIMIT_EXCEEDED | Too many requests — retry after the indicated delay |
IMPORT_PARSE_FAILED / IMPORT_PERSISTENCE_FAILED | Import job encountered errors — check the error report |
ACCOUNT_LOCKED | Too many failed login attempts — try again later |
MODULE_DISABLED | The module owning this route is switched off on this instance |
LICENSE_SUSPENDED | The instance's license is suspended — write operations are blocked (read-only mode) |
*_NOT_CONFIGURED | A per-instance dependency is missing — 409, see Dependency not configured |
SYSTEM_SERVICE_UNAVAILABLE | A configured dependency is momentarily unreachable — 503 with Retry-After |
BILLING_LICENSE_QUOTA_EXCEEDED | 403 — product creation refused: the plan's product cap is reached (updates are never refused). See External API → Product cap |
BILLING_LICENSE_QUOTA_BUSY | 409 — another creation was checking the product cap at the same moment; nothing was written — retry |
VALIDATION_URL_SLUG_TOO_LONG | 422 — url_slug longer than 255 characters (/ext and WinDev writes) |
VALIDATION_URL_SLUG_SCHEME | 422 — url_slug looks like a full address (javascript:…); send only the end of the address. In POST /ext/products/batch it refuses the whole request |
REQUEST_UNKNOWN_FIELD | 422 — the JSON body carries keys the operation does not declare; error.details names them and nothing is written. The public families /ext and /windev never answer it in contract v1: they tolerate unknown keys and list them in champs_ignores |
WINDEV_PRODUCT_IN_TRASH / WINDEV_REFERENCE_AMBIGUOUS / WINDEV_PRICE_TTC_ONLY | WinDev named refusals (409 / 409 / 422 on single routes, error_code on the line of a batch) — see WinDev → Named refusals |
EXPORT_INTEGRATION_ABSENT | 422 — the export destination has no integration on this instance: Google Drive (platform_type: gdrive) can no longer be created, tested, previewed, sent to or scheduled — see Exports |
SUPPLIER_INTEGRATION_ABSENT | 422 — POST /suppliers/test-connection with a Google Drive source (google_drive / gdrive): not offered as a supplier source |
SYNC_FIELD_MAPPING_TARGET_UNKNOWN / SYNC_FIELD_MAPPING_TARGET_PROTECTED / SYNC_FIELD_MAPPING_SOURCE_INVALID / SYNC_FIELD_MAPPING_STRATEGY_UNSUPPORTED | 422 — a field correspondence of a shop or ERP connection is refused when saved: unknown target field, target held by the catalogue (identity, EAN, price, weight, categories…), source outside the allowed list, or strategy not supported by the platform. Nothing is written |
SYNC_PROFIL_INTROUVABLE | 404 — the sync profile does not exist (deleted, or another platform); reload the connection's profiles. See Connectors → Sync profiles |
SYNC_PROFIL_INVALIDE | 422 — a sync profile setting is unreadable or not allowed (name, direction, contents outside the closed list, override outside the allow-list, preview mode, unknown key, scope given at launch when the profile fixes one; since v6.39 mapping_id on an export profile — mapping_envoi_interdit —, unknown or of another connection — mapping_inconnu —, or making the connection's options unreadable — mapping_incompatible —, and {"apercu": true} on a profile that is not an import of products — apercu_import_seulement); details.raison and details.champ name it. Nothing is saved |
SYNC_PROFIL_NOM_PRIS | 409 — a profile with this name already exists on this connection (case ignored). Nothing is created |
SYNC_PROFIL_UTILISE | 409 — the profile is used by schedules (details.plannings): it cannot be deleted, nor change direction or contents, until they are attached to another profile. Nothing is changed |
SYNC_PROFIL_NON_EXECUTABLE | 409 — the profile cannot be launched or attached to a schedule: it would not run as it announces. details.raison: inactif, surcharge_non_appliquee (an override the synchronization does not apply or no unit of the profile reads — listed in details.surcharges_non_appliquees), contenu_partiel_non_pris_en_charge (partial product record the platform or direction does not support — texts / SEO outside PrestaShop / WooCommerce export, media / brand outside PrestaShop / WooCommerce / Magento export; listed since v6.41 in details.contenus_non_pris_en_charge). Since v6.38 overrides and partial records run where supported, and since v6.39 a named import mapping runs. Nothing is launched |
SYNC_PROFIL_INCOMPATIBLE | 422 — profile and schedule do not match: another connection, another direction, a schedule running both directions, or other units; details.raison. Nothing is changed |
MAPPING_TEMPLATE_NOM_PRIS | 409 — since v6.39, a column (mapping) template of the same supplier and import type already has this name (case ignored): creation, rename, duplication, Code2ASIN template without remplacer. details.modele_existant_id names it — replace it or choose another name. Nothing is changed. See Imports → Mapping templates |
VALIDATION_HOST_UNRESOLVED | 400 — a server address you sent (FTP / SFTP host, URL of a source, webhook or platform…) does not resolve: its name matches no address. Check the spelling of the host (no ftp:// scheme or path); details.champ names the field. Distinct, since v6.41, from VALIDATION_URL_REJECTED_SSRF (an address that points to an internal network). Nothing is changed |
EXPORT_SCHEDULES_RETIRED | 410 — the export schedules (/exports/schedules*) are retired: nothing is read or written; use the schedules of a sync profile (see Exports) |
VALIDATION_INVALID_PATTERN | 422 — a value does not match its expected format (for instance an unreadable Magento attribute code in a field correspondence) |
EXPORTS_INSTANCE_INTERDITS | 409 — sending to shops and ERPs is disabled for this instance by the Pixee PIM team (imports stay allowed) |
SYSTEM_INTERNAL_ERROR | Unexpected server error (5xx) |
Public API families (/ext, /windev)
Error responses of /api/v1/ext/* and /api/v1/windev/* (missing or refused key, not found, validation, quota, server error) carry the API-Version: 1 header — and the deprecation headers when the operation is deprecated — like their successful responses. The body is the envelope above; nothing else changes. A refusal returned directly by a middleware (module gate, license, CSRF) may come without these headers: the HTTP status is enough. See External API → Versioning.
Localization
error.message is translated based on the Accept-Language header. Only French (fr) and English (en) have translated message catalogs today, even though the underlying locale middleware accepts a broader set of locales for other purposes.
The response's Content-Language header always announces the language that was actually served, not necessarily the one you requested. If you ask for a locale with no translated catalog, the API falls back to English (or French, depending on configuration) and says so honestly via Content-Language — it does not claim to speak a language it didn't use.
Request a localized error
curl https://api.pixeepim.com/api/v1/products/00000000-0000-0000-0000-000000000000 \
-H "Authorization: Bearer {api_key}" \
-H "Accept-Language: fr"
Async operations
Endpoints that start long-running jobs (imports, exports, enrichment) return 202 Accepted immediately. Poll the job's status endpoint until the status is completed or failed:
202 Accepted
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
For 429 errors, parse the detail field (e.g. "5 per 15 minute") — there is no numeric retry_after_seconds field; retry_after is a human-readable string. See Rate Limiting for the exact format and recommended backoff strategy.