Sign each request with a fresh HMAC signature. The cURL and HTTP header values below are placeholders, not reusable credentials. For runnable signing code, use request signing examples. Python examples use the signed session from that page; Node.js examples use its
bravadoFetch helper.Finding your rate limit
CallGET /v2/trade/account to retrieve your current API key metadata, including your rate limit:
api_key object with a rate_limit_per_min field:
When you hit the limit
When you exceed your rate limit, Bravado responds with:- HTTP status:
429 Too Many Requests - Response header:
Retry-After: <seconds>, the number of seconds you must wait before retrying - Response body:
{"error": "rate limit exceeded"}
Retry strategy
The recommended approach is exponential backoff with jitter, with theRetry-After header taking precedence over any computed delay.
Python
Idempotency keys and safe retries
All mutating requests should include anIdempotency-Key header. If your request times out or you receive a transient error before you know whether it succeeded, you can safely resend the identical request with the same key, Bravado will deduplicate it server-side and return the original result without creating a duplicate order or withdrawal.
- Use UUID v4 values to guarantee uniqueness across requests.
- The same key is safe to reuse only for the same logical operation. Generate a new UUID for every distinct request.
- Idempotency keys are scoped to your API key, so there is no collision risk across accounts.
Idempotency keys protect against double-submission during network retries. They do not bypass rate limits, a deduplicated replay still counts as a request if the original already succeeded.
MCP server limits
The MCP server is limited per connection (your partner account plus the specific OAuth client), not per IP. AI clients reach Bravado through shared egress addresses, so a per-IP limit would pool unrelated customers into one bucket and let the busiest of them throttle everyone else. Two limits apply:
The query budget exists because request counts do not describe cost. A single query can read tens of gigabytes to return ten rows, and only a budget denominated in bytes scanned can bound that. The purpose-built tools are unaffected by it, so an agent that hits the budget can keep working through them.
Every
run_sql result reports how many bytes it actually read, so an agent can pace itself rather than discovering the limit by hitting it.
Best practices
- Batch cancellations, use
DELETE /v2/trade/orders(cancel-all) instead of looping individual cancel requests. A single call consumes one rate-limit slot regardless of how many open orders you have. - Use strategies for high-frequency fills, if you need many small orders placed at regular intervals, use the
interval_secfield on execution strategies rather than calling the orders endpoint in a loop. - Monitor proactively, poll
GET /v2/trade/accountperiodically to confirm yourrate_limit_per_minhasn’t changed and to detect unexpected spikes in your usage patterns. - Share limits across threads, if your integration is multi-threaded or runs multiple processes, coordinate request counting centrally (e.g. via a token-bucket in Redis) to avoid one thread consuming the budget of another.