Rate limits
The Management API has its own budgets. They never use up your trading API budget, and your trading traffic never uses up these.
Budgets
| Budget | Applies to | Sustained rate | Burst |
|---|---|---|---|
| Token | Every request made with your token | 120 per minute | 30 |
| Reads | GET requests | 60 per minute | 20 |
| Changes | PATCH costs 1; creates, deletes and monitor restarts cost 2 | 20 per minute | 10 |
| Per resource | Changes to one existing resource: PATCH, DELETE and monitor restarts. Each copier, each monitor, each account group, each ATM template, your controls, your news lockout and your connection settings has its own budget, so changing many different copiers is limited by Changes, not by this one. Creates are not counted here | 6 per minute | 3 |
| Turning a copier off | PATCH on a copier whose body is exactly {"active": false} | 120 per minute | 60 |
| Kill switch | PUT /controls/kill-switch, both directions | 10 per minute | 5 |
| In flight | Requests running at the same time for your account | 2 |
Requests are also limited per client IP address before your token is checked, across every token used from that address. Normal use never reaches it.
GET /v1/api/manage returns your current budgets in limits: token, read, write (Changes), resource, stop, kill, each with perMinute and burst, and inFlight ({"max": 2}).
Every attempt counts, including a change that is refused or that matches what is already saved (changed: false). This keeps scripts that toggle settings in a loop from hammering your settings.
Turning copiers off quickly
Stopping copying is never held back by your change budget. A PATCH whose body is exactly {"active": false} uses its own budget, does not count toward Changes or Per resource, and is still accepted when the API is under heavy load. Send nothing else in the body: a request that also changes another field is a normal change.
With 50 copiers, send the 50 requests one or two at a time (the in-flight limit is 2). This budget takes 60 at once, but every request also counts toward your Token budget (30 at once, then 2 a second), so 50 copiers take about 10 seconds. Retry any 429 after Retry-After. Turning copiers back on, and every other copier change, uses the normal Changes budget, so re-enabling 50 copiers takes about two minutes.
for id in $(curl -s -H "Authorization: Bearer $TOKEN" https://app.crosstrade.io/v1/api/manage/nt8/copiers \
| jq -r '.data[] | select(.active) | .id'); do
until [ "$(curl -s -o /dev/null -w '%{http_code}' -X PATCH \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"active": false}' "https://app.crosstrade.io/v1/api/manage/nt8/copiers/$id")" != 429 ]; do
sleep 1
done
done
The kill switch has its own operation budget
PUT /controls/kill-switch uses its own Kill switch budget instead of Changes and Per resource, so other changes cannot use it up, and heavy load on the API does not hold it back. The Token budget, authentication and the in-flight limit still apply.
Handling limits
| Status | error | Meaning |
|---|---|---|
| 429 | rate_limited | A budget is used up. policy names it: read, write, resource, stop, token, ip or kill |
| 429 | too_many_concurrent_requests | Two requests are already running for your account |
| 503 | management_busy | The API is under heavy load. Retry after the delay |
The admission errors listed above include a Retry-After header and retryAfter in the body, in seconds. Other 503 errors may omit a retry delay:
{
"success": false,
"error": "rate_limited",
"detail": "Too many requests. Retry after the indicated delay.",
"retryAfter": 7,
"policy": "write",
"meta": { "requestId": "req_9f2c1a0b7d3e4f51" }
}
Wait at least retryAfter seconds before you retry. Budgets refill continuously, so there is no fixed reset time.