Errors & Rate Limits
The error contract for all v2 endpoints: response shapes, an error catalog with retryability, and throttling behavior.
Error response shape
All endpoints use the standard error envelope:
{
"detail": "Authentication credentials were not provided."
}
{
"detail": "Invalid input",
"errors": {
"deposit_currency": ["This field is required."],
"withdraw_currency": ["This field is required."]
}
}
Both keys are optional and can co-occur: detail carries the view-level message, errors maps field names to lists of messages.
One exception: POST /pre_screening/ uses its own {status, message} envelope, including for its 400/401 responses — and compliance rejection there is HTTP 200 with status: "failed".
HTTP status codes
| Code | Meaning | Notes |
|---|---|---|
200 | OK, including an existing DeFi order returned to a legacy integration and pre-screening status: "failed" | |
201 | Order created | |
400 | Validation error | See catalog below for retryability |
401 | Missing/invalid credentials; referral code not owned by the key owner | Authentication |
403 | Access refused (e.g. pre-screening rejected at access level) | |
404 | Resource not found — also used for "no quotable pairs" on /rate/ and for /pre_screening/ without a trusted token | Often temporarily unavailable, not unsupported |
417 | GET /pair/random/ exhausted its attempts | |
429 | Throttled | See Rate limits |
Error catalog
Error identification is currently message-based — there are no machine-readable error codes. Match on the stable substrings below (avoid exact-match: wording can gain IDs/values via interpolation).
Quote & rate errors
| Message (pattern) | Where | Meaning | Action |
|---|---|---|---|
rate_id '<id>' is invalid or expired. | POST /orders/ (DeFi) | The bound quote is gone | Retryable: fetch a fresh quote, retry once with the new rate_id (refresh-on-submit) |
The quote for this rate_id has expired. Please request a new rate. | POST /orders/ (DeFi) | Quote TTL elapsed | Retryable: same as above |
Pair <name> is temporarily unavailable. | POST /orders/ (CeFi) | Covers CeFi quote-validation failures, including expired rate_id | Retryable: fresh quote → retry; if it persists, the pair genuinely lacks liquidity right now |
Unable to fetch a cross-chain fixed-rate quote. Please retry. | POST /orders/ (DeFi cross-chain) | Transient upstream liquidity/quoting failure — despite arriving as a validation error | Retryable with backoff |
The market has moved beyond this rate's tolerance. Please request a new rate. | POST /orders/ (DeFi fixed) | The venue cannot deliver the promised rate within tolerance | Retryable: fetch a fresh quote, retry once with the new rate_id |
Quote provider is unavailable. Please request a new rate. | POST /orders/ (DeFi) | Upstream quoting outage | Retryable with backoff |
The provided fixed-rate rate_id is non-actionable for this route. Request a new actionable rate… | POST /orders/ (DeFi fixed) | The rate_id was issued for a different route | Fix the flow: request a fresh fixed-rate quote for the pair you are ordering |
This rate_id was issued for a DeFi quote and cannot be used to create a standard (CeFi) order. | POST /orders/ | Mode mismatch between quote and order | Fix the request — quote and order must use the same mode |
Promised applied referral rate no longer matches current partner limits; request a new quote (rate_id). | POST /orders/ | Partner fee terms changed between quote and create | Retryable: fresh quote |
This route cannot be quoted right now. Please request a new rate and retry later, or choose another pair. | POST /orders/ (DeFi) | The route's liquidity provider declined to quote it. This can be a temporary outage or a pair it does not serve, and the response cannot tell which | Retryable later: fetch a fresh quote after a pause; if it keeps failing, offer the user another pair |
Request validation errors (not retryable — fix the request)
| Message (pattern) | Where | Meaning |
|---|---|---|
Provide either markup (basis points, preferred) or the legacy referral_rate (decimal fraction), not both. | /rate/, /orders/ | Mutually exclusive fee params |
Must not exceed <N> basis points (the absolute cap on dynamic referral rates). | /rate/, /orders/ | markup above the absolute cap |
referral_rate is not allowed for this referral code | /rate/, /orders/ | Dynamic fee capability not enabled — see Affiliate & Referral |
rate_id is not supported for limit orders. Remove rate_id when is_limit_order is true. | POST /orders/ | Limit orders take no quote binding |
limit_ttl must be at least 600 seconds (10 minutes). | POST /orders/ | Positive limit_ttl below the minimum; omit it to get the default (currently 24 h) |
Deposit amount (amount_quote) is required for cross-chain fixed-rate… | POST /orders/ | Cross-chain fixed-rate needs deposit_amount |
<value> has invalid characters for a valid value | /orders/ address fields, keyed under the field that failed (withdraw_address or refund_address) | Address failed the per-currency format validation. The refund address must be valid for the deposit currency, the withdraw address for the currency received |
Only one of deposit_amount or withdraw_amount can be provided | /pre_screening/ | Amount exclusivity |
Idempotent-conflict errors (safe to treat as success)
| Message (pattern) | Where | Meaning |
|---|---|---|
External deposit notification with tx_id <hash>already exists (sic — no space) | PATCH /orders/{ref}/ with deposit_transaction | The hash was already recorded — your earlier submission worked |
Address validation scope
Addresses are validated per currency format (regex + checksum — EVM checksums, base58, bech32, …). A fundamentally wrong format is rejected, but chains sharing an address format are not distinguished: a valid-format EVM address intended for a different EVM chain passes validation. Verify the address/chain pairing client-side before order creation — wrong-chain payouts risk fund loss.
Rate limits
Global throttle
All v2 endpoints share a global throttle of 2500 requests/minute per API key (and per IP for unauthenticated access). Exceeding it returns:
HTTP 429
Retry-After: <seconds>
{
"detail": "Request was throttled. Expected available in 58 seconds."
}
Honor the Retry-After header when present.
Quote-specific throttles
DeFi fixed-rate orders created without a rate_id quote the venue on every request and have a tighter limit, returned as 429 with:
Too many fixed-rate quote requests. Please retry shortly.
This response may arrive without a Retry-After header — apply your own backoff. Passing the rate_id from GET /rate/ avoids it.
Client-side guidance
- Poll order status at 30–60 s intervals (Order Tracking) — well within limits even for hundreds of concurrent orders.
- Never fetch the unfiltered rate catalogue in a loop (why).
- DeFi fixed-rate order creation quotes the venue live and can take several seconds. Set request timeouts of 15 s or more for
POST /orders/on DeFi fixed-rate flows, and after a client-side timeout check whether the order was created before sending the request again (why).
Next steps
- Rates & Pricing — the refresh-on-submit pattern these errors assume
- Order Tracking — polling within the limits