Errors & Limits
Error frames
Errors come in two shapes depending on scope.
Connection-scope errors arrive as a standalone frame and may close the connection:
{ "type": "error", "code": "internal_error", "message": "..." }
Command-scope errors are returned against the command that caused them (the success: false ack):
{
"method": "subscribe",
"success": false,
"error": { "code": "unknown_symbol", "message": "Unsupported symbol XAU/USD" },
"req_id": 1
}
Error codes
| Code | Scope | Meaning |
|---|---|---|
auth_failed |
connection | API key missing/invalid/expired, IP mismatch, account deactivated, or credential revoked mid-connection. Connection closes. |
unknown_symbol |
command | Subscribed to a pair that isn't supported. |
rate_limited |
connection | Dropped as a slow consumer (your client wasn't reading fast enough). |
invalid_message |
connection or command | Malformed JSON arrives as a standalone connection-scope frame ({ "type": "error", "code": "invalid_message" }); an unknown method arrives as a command-scope ack (success: false) against the offending command. Neither closes the connection. |
Server-side faults are not surfaced as a frame on an open connection — they are logged server-side. If the connection closes unexpectedly, reconnect with backoff (see Connecting). Faults during the connection upgrade surface as an HTTP
500on the upgrade response (see below).
Upgrade rejection (HTTP)
Auth and quota failures happen before the WebSocket opens, so they surface as HTTP status codes on the upgrade response, not as frames:
| Status | Reason |
|---|---|
401 |
Missing/invalid X-Api-Key, IP mismatch, or deactivated account |
429 |
Rate limit exceeded (see below) |
500 |
Server-side fault while handling the upgrade. Retry with backoff. |
Rate limits
| Limit | Default |
|---|---|
| Concurrent connections per account | 5 |
| Connection attempts per minute | 60 |
Exceeding either rejects the upgrade with 429. If you need higher limits, contact support.
The concurrent-connection cap is enforced per server instance, so during scale-out an account may briefly hold more than the listed number across instances. The connection-attempt limit is enforced globally. Don't rely on the connection cap as a hard ceiling — size your client to the listed value.
Mid-connection revocation
Credentials are re-checked periodically while connected. If your API key is revoked or your account is deactivated, the connection is closed with auth_failed within ~25 seconds — you do not need to reconnect to pick up a revocation.
Slow consumers
If your client stops reading and the server's send buffer for your connection backs up beyond its limit for more than a few seconds, the connection is dropped with rate_limited. Read frames promptly (and reply to pings) to avoid this. On a drop, reconnect with backoff and re-subscribe.
Versioning
The feed is versioned in the URL path (/v1/rates). /v1 is stable; any breaking change ships under a new version.