Conventions

Error codes and problem+json, rate limits and Retry-After, idempotency keys, paging, and incremental polling — the rules that hold across every endpoint.

These rules hold across every endpoint. Reading this page once will save you reading the same paragraph on each of the others.

Errors

Failures come back with a real HTTP status and an application/problem+json body (RFC 9457). Branch on code, which is stable, rather than on the prose in title or detail.

{
  "type": "urn:marketplacehub:error:validation-failed",
  "title": "The request is not valid",
  "status": 400,
  "code": "validation-failed",
  "requestId": "6a730dcb444667a4a2a03fc5",
  "errors": { "OrderItems[0].OrderItemId": ["Not a valid MarketplaceHub id."] }
}
CodeStatusMeans
validation-failed400Something about the request is wrong. errors names the fields.
unauthenticated401No usable token, or the token names no resolvable account.
insufficient-scope403Valid credential, but it was not granted this permission.
not-found404No such route, or no such record under one that exists.
method-not-allowed405The route exists but not for that HTTP method.
conflict409The request conflicts with current state — see idempotency below.
unsupported-media-type415The body was not JSON.
quota-exceeded429You are over your request allowance.
internal-error500Something failed on our side.

requestId identifies the row for that call in API → Logs in the app. It is on successful responses too. If you ever need to ask us about a call, that is the number to quote.

We deliberately do not return exception text. A 500 gives you a requestId and nothing else — the detail is in our logs, where it cannot leak our internals to you or yours to anybody else.

Rate limits

Each endpoint has its own allowance: a burst of 10 requests that refills at 1 per second. Being quiet for ten seconds buys you a full burst again.

Over the limit you get 429 with a Retry-After header in whole seconds. Wait that long — do not retry immediately, and do not retry in a tight loop. The limit is shared across all our servers, so a second machine will not get you a second allowance.

You can see the current state of your allowance in API → Request Throttling in the app.

Idempotency

Every write endpoint accepts an Idempotency-Key header. Use it. A timeout does not tell you whether the work happened, and without a key a retry can confirm a shipment twice or refund a buyer twice.

Idempotency-Key: 8f14e45f-ea6d-4d1b-9c1e-3f0f6d5b7a21

Rules:

  • Same key, same request → you get the original response back, byte for byte, including its requestId. The work is not repeated. The response carries Idempotent-Replay: true.
  • Same key, different request → 409 conflict. A key belongs to one request; reusing it for another is a bug we refuse rather than guess at.
  • Same key while the first attempt is still running → 409 conflict. Retry shortly.
  • Keys are remembered for 24 hours and are scoped to your account.
  • A 500 releases the key, so a genuine retry can proceed.

Use a fresh key per logical operation — a UUID, or your own order reference. Do not reuse one across different payloads.

Paging

Nothing returns an unbounded list. Omit a limit and you get a sensible page, not everything.

Orders page with a cursor: send the NextCursor from each response back as Cursor until HasMore is false. A cursor walks a changing set without repeating or skipping rows, which an offset cannot.

Products page with an offset against TotalCount. The catalogue search is offset-based underneath, and we would rather tell you that than pretend a cursor is available where it is not.

Asking for more than an endpoint's maximum is refused with 400 rather than quietly reduced — a silently smaller page reads as "that is all there is", and an integration that believes it stops early and loses records.

Polling for changes

Use UpdatedSince on the orders list rather than re-pulling a date range. It is inclusive: pass the largest LastModifiedDate you have seen, and expect to see those boundary orders again. Dropping a repeat by OrderId is cheap; missing an order is not.