The error object
Every error has the same shape and an HTTP status that matches it:A failed run isn’t an error
A generation that fails, because the model refused the prompt or the provider had an error, isn’t an HTTP error. Starting it succeeded, and reading it returns200 with status: "failed" and an error.message. It isn’t charged. Check status before you read output. See Generations.
Error codes
invalid_parameter
400. A field in the body or the query, or the idempotency-key header, isn’t valid: missing, the wrong type, not one of the allowed values, or not an input of this model at all. param names the field. For a field of a model’s input, message also says what’s allowed. A body that isn’t valid JSON gets this code too, with param null and the message Invalid request parameters. Fix the request; retrying it unchanged fails again.
invalid_request
A4xx that has no code of its own on this page, with the status that fits it. You’ll rarely see one. Fix the request before you send it again.
conflicting_credentials
400. The request carried two different API keys, one in x-api-key and one as Authorization: Bearer. Send one of them. The same key in both headers is fine.
placeholder_in_path
400. The path still holds a placeholder from an example, such as {id}, <id>, :id or undefined, so the request never reached an endpoint. Put the real ID there.
missing_api_key
401. The request carried no API key. Send it in the x-api-key header or as Authorization: Bearer. The message also names a common slip, such as an empty header or a shell variable sent as its name ($LEAP_API_KEY that never expanded). See Authentication.
invalid_api_key
401. The key isn’t valid: mistyped, cut short, revoked or expired. The message says which mistake its shape shows, such as Bearer inside x-api-key, or a key that doesn’t start with leap_. Create a new key at app.tryleap.ai/go/api if yours is gone.
authentication_required
401. Authentication is required. On /v1, a missing or invalid key gets missing_api_key or invalid_api_key instead, so you’ll rarely see this one.
insufficient_credit
402, type billing_error. Your available balance doesn’t cover the run’s hold, or an upload of a video or a sound needs credit. Nothing started. Add credits at app.tryleap.ai/go/settings/credits, or get a quote to see what a run needs.
permission_denied
403. The key doesn’t have the scope this endpoint needs. The message names the scope.
resource_not_found
404. Nothing with that ID exists in your workspace. IDs from another workspace are a 404 too.
not_found
404. No endpoint matches the path. The message points the way: a path without /v1 gets the /v1 one, a habit from another API gets the nearest Leap endpoint, and an OpenAI path such as /v1/chat/completions is told that Leap isn’t OpenAI-compatible and that runs start at POST /v1/generations.
method_not_allowed
405. The endpoint exists but doesn’t take this HTTP method. The Allow header lists the methods it does take.
conflict
409, type conflict_error. Either the idempotency-key was already used with a different body (use a new key for a new request), or the generation can’t be canceled anymore (see Cancel).
gone
410. The model or preset was retired at its sunset date and doesn’t run anymore. Its own catalog entry, GET /v1/models/{creator}/{name}, answers with this 410 too, so you can’t look up the replacement there. The message names it, when there is one: {id} was retired on {sunset}. Use {replacement} instead.
To move before that happens, watch for deprecation. While a model is deprecated, its catalog entry’s deprecation.replaced_by names the replacement, and every answer about it carries the deprecation headers.
payload_too_large
413. The body is over its limit, such as an image over 4 MB. Send a video or a sound through a direct upload.
unsupported_media_type
415. The body’s content type isn’t one this endpoint takes.
rate_limit_exceeded
429, type rate_limit_error. Your key sent too many requests, or a workspace limit was reached, such as the daily upload allowance. The answer says how many seconds to wait in Retry-After; wait that long, or a minute if it’s missing, then retry with the same idempotency-key.
ip_rate_limit_exceeded
429, type rate_limit_error. Too many requests came from your IP address. Wait for Retry-After seconds, then retry.
internal_error
500, type api_error. Something failed on our side. Retry with backoff and the same idempotency-key. If it keeps failing, contact support with the request_id.
service_unavailable
503, type api_error. The API or this model is out of service for a while. Wait for Retry-After seconds when it’s set, otherwise back off, then retry.
Retries
Which requests are safe to repeat
GET requests and quotes change nothing, so they’re always safe to retry.
Only POST /v1/generations and POST /v1/batches read the idempotency-key header. Send a new key with each new request, and the same key when you retry it: the retry gets back what the first request started, and nothing runs or is charged twice. A key is 1 to 255 printable ASCII characters; anything else gets a 400 with param set to Idempotency-Key. See Retry safely.
Other POST requests ignore the header:
- Retrying
POST /v1/filesstores a second copy of the image, with its own ID. - Retrying
POST /v1/uploadsopens a second upload, which counts toward the 10 uploads in flight and the 5 GB a day. - Completing an upload, canceling a generation and keeping an output as a file are safe to repeat: doing one twice has the same effect as doing it once.
A retry helper
This wrapper retries network errors,429 and 5xx up to five times. It waits as long as Retry-After asks, or backs off exponentially with jitter, and gives up at once when the API asks for a wait longer than a minute, such as a daily allowance that resets at midnight UTC. A proxy in front of the API can answer a 502 with an HTML page, so it reads the body as text before it parses it.
Rate limits
Every answer carries your IP budget in the
RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers. RateLimit-Reset is the number of seconds until the window resets. To stay under the limits, wait in the request with prefer: wait instead of polling fast, poll each run every few seconds at most, and start many runs with one batch.