Skip to main content

The envelope

Every JSON response shares one shape:
Always check status before reading data. Branch on code, a stable string, rather than on the HTTP status alone. The HTTP status conveys the category; code narrows it down.

REST error codes

For validation_error, branch on the inner reason: the keys of data.validation_errors, or non_field_errors for cross-field rejections. Each endpoint in the API reference documents the concrete codes it emits.

WebSocket errors

Command errors arrive in-band and leave the socket open:
Treat any other code as opaque: log it and retry with backoff. Handshake errors. A refused connection returns a non-101 status with a short plain-text body: Treat any other 4xx as 401 and any other 5xx as 503. Backpressure. If you read too slowly, the server’s outbound buffer overflows and it closes the connection with code 1008. A client that has stopped reading never receives that frame and sees only a TCP close. Read faster or register fewer events: an immediate reconnect overflows again. Silent drops. Two failures close an established connection with a raw TCP close and no close frame: an I/O error and an internal error. A quiet socket is not proof of a healthy one. Always reconnect with backoff and re-register your events. See WebSocket stream.

Rate limits

Limits are per account. All of an account’s API keys draw from one budget. Each limit is a token bucket that refills continuously, so capacity returns steadily rather than at a fixed reset, and an idle account can spend a full bucket in one burst. The two placement rows are dedicated budgets. A betslip or order creation counts only against its own limit, not the general one. For the betslip budget, every create counts, including a rejected one. A refresh does not count. The budget refills continuously, with no fixed reset. Every other endpoint, market data included, shares the general budget, so polling GET /v2/events/ or GET /v2/offers/ never throttles order placement but leaves less for other GETs. Budgets are keyed on the account, not the source address, so spreading an integration across several hosts does not add capacity. The WebSocket stream has no message-rate limit; its connection-level limits are the registered-event cap and the slow-reader disconnect. The GET /v2/offers/ row counts open requests, not a rate. One call can name up to 20 events at the same cost against the cap, so one call naming 20 events costs less than 20 calls naming one. A request over the cap gets 429 throttled with a fixed retry_after of 1 second: retry when one of your open requests returns. A rejected request gets 429 throttled with the wait in Retry-After and in data.retry_after. Success responses carry no remaining-quota header, so track your own rate. If your integration needs more headroom, email support@magicmarkets.com and tell us the volume you expect.

Common symptoms

Retry strategy