Skip to content

Rate limits & errors

Every /api/v1/vendor route is rate-limited using a sliding window (4 sub-segments — a burst right at a window boundary is smoothed, not doubled), partitioned per authenticated caller (your credential’s client id), falling back to caller IP only on the anonymous token endpoint itself.

Route Limit Window
POST /api/v1/vendor/auth/token 10 requests 60 seconds
Every other /api/v1/vendor/** route 120 requests 60 seconds
HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json
{ "state": "RATE_LIMITED", "payload": null, "details": { "message": "Too many requests. Please try again later." } }

Respect the Retry-After header (seconds) rather than retrying immediately or on a fixed-interval guess. If you’re hitting the token endpoint’s 10/min limit, you almost certainly have a bug where you’re fetching a fresh token on every request instead of caching it (see Authentication & capabilities). The vendor data-route budget (120/min) is intentionally more conservative than the partner API’s own 300/min — vendor integration traffic is typically lower-volume (offer/stock/cost pushes, order polling) than partner order/cart traffic.

Every response — success or failure — is wrapped the same way:

// success
{ "state": "SUCCESS", "payload": { "...": "..." } }
// created (201 responses)
{ "state": "CREATED", "payload": { "...": "..." } }
// error
{ "state": "SOME_ERROR_CODE", "payload": null, "details": { "...": "optional context" } }

state is always present. On an error, it carries a stable, machine-readable code — parse and branch on state, not on the HTTP status code alone.

HTTP status Meaning
200 / 201 Success.
400 Request validation failed (a malformed/missing/out-of-range field).
401 Missing, invalid, or expired credentials/token.
403 Authenticated, but your token doesn’t carry the capability this endpoint requires.
404 The resource doesn’t exist — or belongs to another vendor. Cross-vendor access always reports 404, never 403, so a not-yours resource is indistinguishable from a genuinely nonexistent one.
422 The request was well-formed but violates a business rule (e.g. the webhook subscription cap).
429 Rate-limited.
5xx Something went wrong on our side — safe to retry with backoff.
Code HTTP When
UNAUTHORIZED_INVALID_CREDENTIALS 401 Wrong clientId/clientSecret at the token endpoint.
UNAUTHORIZED_VENDOR_INACTIVE 401 Your vendor account is inactive.
FORBIDDEN_CAPABILITY_NOT_GRANTED 403 Your token doesn’t carry the capability this endpoint requires — see details.requiredCapability.
NOT_FOUND_VENDOR_OFFER 404 Unknown offer id, or one belonging to another vendor.
NOT_FOUND_FULFILLMENT_GROUP 404 Unknown fulfilment group id, or one belonging to another vendor.
NOT_FOUND_VENDOR_WEBHOOK_SUBSCRIPTION 404 Unknown subscription id.
VALIDATION_WEBHOOK_EVENT_TYPE_NOT_SUBSCRIBED 400 Test-firing an event type your subscription isn’t subscribed to.
BUSINESS_VENDOR_WEBHOOK_SUBSCRIPTION_LIMIT 422 You already have 5 active subscriptions.
RATE_LIMITED 429 See above.
  • Never distinguish “wrong client ID” from “wrong secret” at the token endpoint — both are the same generic UNAUTHORIZED_INVALID_CREDENTIALS, to avoid letting a caller enumerate valid client IDs.
  • Never leak a stack trace, an internal exception message, or an implementation detail in details.
  • Never return a 200 with an error state embedded in the payload — if state isn’t SUCCESS/CREATED, the HTTP status code is always a matching 4xx/5xx too.