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 idempotent DeFi order reuse 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 |
Unable to fetch actionable same-chain DeFi fixed-rate quote for the provided deposit_amount. Please retry. | GET /rate/ (actionable) | Transient RFQ failure | Retryable with backoff (respect the ~15s refetch cooldown) |
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) | An indicative quote was used where an actionable one is required | Fix the flow: request an actionable quote |
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 |
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 | Address failed the per-currency format validation |
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 (RFQ) quoting has additional, tighter limits, returned as 429 with:
Too many same-chain fixed-rate quote requests. Please retry shortly.Too many cross-chain fixed-rate quote requests. Please retry shortly.
These responses may arrive without a Retry-After header — apply your own backoff. Additionally, identical same-chain actionable quote requests are internally cooled down for ~15 seconds; don't re-request an actionable quote for the same pair/amount more often than that.
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).
- Actionable RFQ quotes can take several seconds to produce — set request timeouts of 15 s or more for
GET /rate/?actionable_quote=trueandPOST /orders/on DeFi fixed-rate flows, and treat client-side timeout + retry via the idempotency/refresh patterns above.
Next steps
- Rates & Pricing — the refresh-on-submit pattern these errors assume
- Order Tracking — polling within the limits