Limits
Two kinds of limits: request rates and field sizes. Both return machine-readable errors.
Rate limits
The API enforces 10,000 requests per minute per account across all /api/v2 endpoints, shared by all API keys and dashboard sessions on the account. Authenticated responses include tracking headers:
Every figure on this page is the allowance you can rely on, not an exact ceiling: you will never be throttled below it, and in some deployments we allow more. Design against the published number and use the response headers below for the live figure.
Past the limit, requests return 429 with a Retry-After header (seconds to wait):
{
"error": {
"type": "rate_limit_error",
"message": "Rate limit exceeded. Retry after 3 seconds.",
"code": 429,
"request_id": "req_abc123",
"doc_url": "https://m8tes.ai/docs/limits#rate-limits"
}
}The SDKs retry 429 and 5xx responses with exponential backoff for idempotent methods (GET, PUT, DELETE), and for the run-creating POSTs, which carry an idempotency key so a retry can never start a second run. Any other POST or PATCH is not retried, to prevent duplicate side effects. A RateLimitError only reaches your code once all retries are exhausted.
Idempotency
A run costs money. A timed-out request must not leave you guessing whether you were charged. Send an Idempotency-Key header on any run-creating POST. A retry returns the run the first attempt made, instead of starting a second one:
curl https://api.m8tes.ai/api/v2/runs \
-H "Authorization: Bearer m8_..." \
-H "Idempotency-Key: order-4242-attempt-1" \
-H "Content-Type: application/json" \
-d '{"message": "Close open tickets", "stream": false}'Supported on POST /runs, POST /runs/first-session, POST /runs/with-files, POST /runs/{id}/reply, POST /runs/{id}/reply/with-files, and POST /tasks/{id}/runs.
- Same key, same request →
200with the original run's current state and anIdempotent-Replay: trueheader. Safe to repeat as often as you like. - Same key, different request →
409witherror.details.error_code = "idempotency_key_reuse". A key is never silently replayed against a call you did not make. - Keys last 24 hours and are scoped to your account, so two accounts can use the same key string without interfering.
- Failed requests do not bind the key. A
402,429, or500leaves the key free. Fix the cause and retry with the same one.
The SDKs generate a key for every runs.create, runs.start_first_session, runs.reply, and tasks.run automatically, which is what makes their retries safe. Pass your own when a retry must survive a process restart:
run = client.runs.create(
message="Close open tickets",
stream=False,
idempotency_key=f"nightly-sweep-{job_id}",
)A replayed streaming call is answered as JSON rather than SSE (the run already exists; there is no fresh stream). The SDKs join the existing run's stream. If that run has already finished, they raise with the run id (idempotent_replay_terminal); fetch its result with runs.get(id).
Multi-tenant: per-end-user rate limits
Rate-limit each end-user's agent turns (run starts and replies) independently, so one end-user bursting can't consume the account:
client.settings.update(per_end_user_rate_per_minute=10)Past the rate, run creation and replies for that user_id return 429 with error.details.error_code = "END_USER_RATE_LIMITED" (plus end_user_id, limit_per_minute, retry_after, and a Retry-After header). Other end-users and your own runs are unaffected. Off by default (null); send null to turn it off again. Related per-end-user run and cost caps: Users.
Advanced: auth endpoint limits (IP-based)
Login, registration, and password reset endpoints have a separate limit of 10 requests per minute per IP address:
POST /v1/auth/loginPOST /v1/auth/registerPOST /v1/auth/forgot-passwordPOST /v1/auth/reset-passwordPOST /v1/auth/verify-email
POST /api/v2/signup (agent-driven signup) has its own IP limit of 10 requests per minute.
Unauthenticated /api/v2 requests (no key, expired/revoked session, or unknown key) are throttled per IP at 120 requests per minute. Most can only return 401; fix the credential rather than retrying.
Verifying an unrecognised credential is metered by this same allowance (charged before the check, refunded when it turns out to be a real account). A burst of bad keys from one address exhausts it, not ordinary authenticated traffic.
A few public endpoints (plan catalogue, agent-template list, POST /api/v2/token) are also counted here. A revoked credential keeps counting against its old account for up to ten seconds (rate-limit attribution is cached), then against your IP. It is rejected with 401 either way.
These endpoints do not require authentication, so limiting is keyed by IP rather than account, and the response uses a different format:
{
"detail": "Too many requests. Retry after 30 seconds."
}Field limits
Request fields have size and count caps:
Webhook payloads and inbound events are capped at 256 KB; larger requests return 413.
The metadata field accepts any JSON object under 10 KB serialized. Use it for IDs, labels, and lightweight state, not for large blobs or document content. When a field exceeds its limit, the API returns 422:
{
"error": {
"type": "validation_error",
"message": "metadata must not exceed 10240 bytes serialized",
"code": 422
}
}Next: Errors · Going Live