Reference
Rate limits & errors
Every SuperCool API error code, what it means and how to fix it, plus rate limits, rate-limit headers, request ids and the request inspector.
#Error shape
Every error has the same JSON shape and an HTTP status that matches it:
{
"error": "idempotency_conflict",
"message": "That Idempotency-Key was already used for a different message. Use a new key for a new request.",
"request_id": "req_6c2f0e1d9a8b7c6d5e4f3a2b",
"docs": "https://supercool.com/docs/api/errors#idempotency_conflict"
}| Field | Meaning |
|---|---|
error |
A stable, machine-readable code. Branch on this, never on message. |
message |
What went wrong, written for people. It may change. |
request_id |
The request's id, also in the Request-Id header. |
docs |
A link to the code's entry on this page. |
Some errors add fields: invalid_request lists its problems, and poll_in_progress includes the cursor to use when you call again.
Outcomes of the work itself, like running out of credits, hitting the running-jobs cap, a failed job or a question from the agent, are not errors. They come back as a message with a status.
#Request ids and the inspector
Every response, success or error, has a Request-Id header (req_…). Log it. The API dashboard's request inspector shows every request from the last 30 days: time, key, method, path, status, error code, latency, and the request and response bodies (redacted and capped). Search it by request id or message id, and filter by key, status, path or error code.
If you contact support, include the request id.
#Rate limits
| Limit | Value | Applies to |
|---|---|---|
| Requests | 600 per minute | Each API key, every request |
| Messages | 120 per hour, 1,000 per day | Each account, POST /v1/messages |
| Waits | 1,500 per hour | Each account, long-polls (?wait= and /events) |
| Work reads | 600 per hour | Each account, GET /v1/work/{work_id} |
| Running jobs | 5 at once | Each account. Not an HTTP error: see below. |
The per-key budget means one busy or leaked key can't use up the whole account. The account limits are generous, and are there to stop runaway loops.
#Running jobs
Up to 5 jobs can run at once per account. The cap never refuses a request: POST /v1/messages always gets through, so you can always stop work or follow up on it. Instead, if the agent tries to start new work while 5 jobs are running, that start is refused. The message comes back as usual (201) with a job whose status is blocked and reason is too_many_running, and the message lists the work that's running in running. See At capacity.
#Headers
Every authenticated response reports the per-key budget:
| Header | Meaning |
|---|---|
RateLimit-Limit |
Requests this key may make per minute (600). |
RateLimit-Remaining |
Requests left in the current window. |
RateLimit-Reset |
Seconds until the oldest counted request leaves the window. |
Retry-After |
On every 429, seconds to wait before retrying. |
Every 429 carries Retry-After. For the per-key budget it's the exact number of seconds until a request frees up. For the account limits (messages, waits, work reads) it's 60.
#Handling 429s
import random
import time
import requests
def call(method, url, **kwargs):
for attempt in range(6):
r = requests.request(method, url, timeout=60, **kwargs)
if r.status_code not in (429, 500, 503):
return r
wait = float(r.headers.get("Retry-After", 2 ** attempt)) + random.random()
time.sleep(wait)
return rAlways send an Idempotency-Key on POST /v1/messages, so a retry can never start work twice.
#Status codes
| Status | Meaning |
|---|---|
200 |
OK. Also returned when an Idempotency-Key replays an existing message. |
201 |
Created: a new message, upload or webhook endpoint. |
400 |
The request can't be processed as sent, including requests that don't match the schema (invalid_request). Fix it before retrying. |
401 |
The API key is missing, unknown, expired or revoked. |
404 |
Nothing with that id for this key or account. |
409 |
Conflicts with the current state. |
413 |
A file is too large. |
422 |
An uploaded file was refused (executable_refused). |
429 |
A rate limit. Always has Retry-After. |
500 |
Something broke on our side. Retry with backoff. |
503 |
The API is temporarily switched off. Retry later. |
#Error codes
#400: Bad request
#bad_request
The request is invalid in a way not covered by a more specific code, for example an unusable webhook setting. Read message for details.
#invalid_request
The request doesn't match what the endpoint expects: a required field is missing, a field has the wrong type, a query parameter is out of range (like wait above 45), or the body isn't valid JSON. The response lists what's wrong in problems, up to 10 entries:
{
"error": "invalid_request",
"message": "The request doesn't match what this endpoint expects; see `problems`.",
"request_id": "req_6c2f0e1d9a8b7c6d5e4f3a2b",
"docs": "https://supercool.com/docs/api/errors#invalid_request",
"problems": [
{ "field": "files", "problem": "Input should be a valid list" },
{ "field": "wait", "problem": "Input should be less than or equal to 45" }
]
}field is the dotted path to the field (like files.0.url or size), or null when the problem is with the body as a whole. Fix the request; retrying it unchanged won't help.
#empty_message
POST /v1/messages had no message, files or upload_ids. Send at least one.
#too_long
The message text is over 20,000 characters. Send the long part as a file instead.
#bad_cursor
The cursor on GET /v1/messages/{id}/events isn't valid for this API key: it's malformed, or it came from another key. Use the cursor from your last page, or leave it out to start from the beginning of the message.
#bad_url
A webhook endpoint URL was refused. It must be a public https URL on port 443 or 8443, with no username or password in it, and at most 2,000 characters. Private and internal addresses aren't allowed.
#bad_events
A webhook endpoint listed an unknown event. Use "*" or events from the list.
#bad_size
POST /v1/uploads needs size: the file's exact size in bytes, as a positive whole number.
#too_many
A message named more than 10 upload_ids. Split the files across messages, or combine them (for example, into a zip).
#401: Unauthorized
#unauthorized
The Authorization header is missing, or the key is unknown, expired or revoked. Send Authorization: Bearer sc_key_… with an active key from the dashboard. See Authentication.
#404: Not found
#not_found
Nothing with that id is available to you:
- Messages belong to the API key that sent them, and are kept for at least 14 days after their last job's deadline. Check you're using the same key, or read the work instead.
- Work, files, uploads and webhook endpoints belong to your account. Check the id, and that the chat wasn't deleted.
webhook_endpoint_idon a message must name an endpoint on your account.
#409: Conflict
#idempotency_conflict
The Idempotency-Key was already used, within 7 days and with this API key, for a message with a different body. Use a new key for a new request. See Idempotency.
#poll_in_progress
Another GET /v1/messages/{id}/events wait is already running for this message. Only one runs at a time. Wait for it to return, then call again with the cursor from the error.
#too_many_endpoints
The account already has 10 webhook endpoints. Delete one you don't need, or use one endpoint for several purposes with per-message routing.
#not_pending
The upload can't be completed because it was rejected or already attached to a message. Create a new upload.
#not_uploaded
You completed an upload before the file reached storage, or its 15-minute presigned POST expired. POST the file to the upload's url first, or create a new upload.
#changed
The uploaded file was replaced while it was being completed. Upload it again with a new upload.
#upload_not_ready
A message named an upload that isn't ready: not completed yet, or not on your account. Call POST /v1/uploads/{id}/complete first.
#413: Too large
#too_large
A file is over the limit: 500 MB per upload, or 1 GB of uploads per message. For inline files, oversized files are skipped and listed in the message's notes instead.
#422: Unprocessable
#executable_refused
The uploaded file is an executable program, which can't be attached. POST /v1/uploads/{id}/complete refuses it and deletes it. This is the only 422 the API returns. (An executable sent inline in files doesn't fail the message: it's skipped and listed in the message's notes.)
#429: Too many requests
#rate_limited
A rate limit was hit: this key's 600 requests per minute, or one of the account's limits on messages, waits or work reads. Wait the Retry-After seconds (exact for the per-key budget, 60 for account limits), then retry. See Rate limits.
#500 and 503
#internal
Something went wrong on our side. Nothing you sent was lost. Retry with backoff, using the same Idempotency-Key. If it keeps happening, contact support with the request_id.
#unavailable
The API is temporarily switched off, for example during an incident. message says why. Retry later.