Error response shape
All errors (other than CORS preflight) return JSON in the OpenAI-compatible envelope:messageis always present.typeis always present. Values mirror OpenAI’s vocabulary:invalid_request_error,authentication_error,rate_limit_exceeded,internal_error, plusinsufficient_quotaon429responses.codeis a machine-readable string (e.g.invalid_purpose,file_not_found,insufficient_quota).nullwhen not meaningful.paramnames the offending parameter ("model","messages","input_file_id", …) or isnull.lineis included for JSONL validation failures duringPOST /v1/batchesto point at the offending line (1-based).
x-request-id
response header (format req_<token>). Pass it along when reporting
issues to support so the call can be located in logs.
HTTP status codes
| Status | Meaning |
|---|---|
200 | Success. |
204 | Success with no body (CORS preflight). |
400 | Your request is malformed or fails validation. Fix and retry. |
401 | Missing x-api-key or x-project-id header. |
403 | Headers were supplied but credentials are invalid, inactive, or don’t belong to the supplied project. |
404 | The resource doesn’t exist, or doesn’t belong to your project. Both cases return 404 to avoid leaking cross-project information. |
413 | Uploaded file is larger than 100 MB. |
429 | Rate-limit / insufficient quota. The error.type distinguishes: insufficient_quota (no balance and no free credits) vs rate_limit_exceeded (transient throttling). |
500 | Internal server error. Retry with exponential backoff; if persistent, contact support. |
Quota errors return
429Catch 429 for both quota exhaustion (error.type:
“insufficient_quota”) and rate-limiting (error.type:
“rate_limit_exceeded”). This matches OpenAI’s behaviour and works
with the official SDK’s built-in retry logic.Common synchronous errors
401 Unauthorized, missing headers
x-api-key or x-project-id was not sent.
Fix: Send both headers on every request.
403 Forbidden, invalid credentials
x-project-id.
Fix: Verify the key, and verify the project UUID matches.
429 Insufficient Quota
Important: quota is checked at line-execution time. If your balance drops to zero during a batch, the remaining lines fail witherror.code: "insufficient_quota"in the error file (the JSONL line’serror.messageincludes the legacy[legacy:http_420]prefix for forensics). The batch as a whole still transitions tocompletedonce all lines have been accounted for.
404 Not Found, file or batch
404 is returned in all three cases.
Fix: Verify the ID and the x-project-id you’re sending.
Files API errors
| Status | Code | When |
|---|---|---|
400 | invalid_content_type | Upload request was not multipart/form-data. |
400 | invalid_multipart | The multipart body could not be parsed. |
400 | invalid_purpose | purpose was anything other than "batch". |
400 | missing_file | No file field in the multipart body. |
400 | empty_file | The uploaded file had zero bytes. |
400 | invalid_limit | List limit query parameter was not a positive integer. |
404 | file_not_found | File does not exist or is not accessible. |
413 | file_too_large | File exceeded 100 MB. |
500 | storage_error | Backing storage error; retry. |
Batches API. POST /v1/batches validation errors
When you create a batch, the service streams the entire input JSONL and
validates every line synchronously before returning a response. Any
failure returns 400 with the offending line (1-based, mirroring
OpenAI’s BatchError.line). The batch is also persisted with
status: "failed" and a populated errors list, so subsequent
GET /v1/batches/{id} calls can introspect what went wrong.
message | Cause |
|---|---|
input_file_id is required | Missing or non-string input_file_id field. |
endpoint is required | Missing or non-string endpoint field. |
completion_window must be "24h" | Anything other than "24h" was supplied. |
Input file not found: <id> | input_file_id doesn’t exist or isn’t in this project. (404) |
Input file object missing: <id> | DB row exists but the stored object is gone. Report to support. (404) |
Input file contains no JSONL lines | Uploaded file is empty or only whitespace. |
Input file exceeds maximum size of 209715200 bytes | File total > 200 MB. |
Input file exceeds maximum of 50000 lines | More than 50,000 lines. |
Line N is not valid JSON | Line N could not be parsed as JSON. |
Line N must be a JSON object | Line N parsed as an array or primitive. |
Line N exceeds maximum size of 1048576 bytes | Line N > 1 MB. |
Line N is missing custom_id | Missing or non-string custom_id. |
Line N duplicates custom_id "X" | A previous line already used custom_id X. |
Line N method must be "POST" | method (case-insensitive) was not "POST". |
Line N is missing url | Missing or non-string url. |
Line N url "X" is not an allowed batch endpoint | url is not /v1/chat/completions, the only supported batch endpoint. |
Line N url "X" does not match the batch endpoint "Y" | Lines in the file disagree on url. |
Line N is missing body | body is not a non-empty object. |
Line N has stream=true; streaming is not supported in batch mode | body.stream === true. |
endpoint "X" does not match the url "Y" used by the input file | The endpoint field on the create request disagrees with what’s in the file. |
line in the JSON body so you can locate and
fix the offending line:
Errors that appear in the error_file_id JSONL
A line can fail after the batch was successfully created. Each failed
line is recorded in the error file with the line’s original custom_id and
an error object. These are the codes you’ll see:
error.code | Triggered when | Recovery |
|---|---|---|
invalid_request_error | The underlying endpoint rejected the line’s body (bad shape, missing field, schema violation). Legacy prefix on error.message: [legacy:http_400] or [legacy:http_422]. | Fix the line’s body and re-submit just that line. |
authentication_error | The API key used to submit the batch lost permission mid-flight, was deactivated, or otherwise became unusable. Legacy: [legacy:http_401] / [legacy:http_403] / [legacy:api_key_unavailable]. | Verify key status; issue a new key if needed. |
not_found_error | A resource referenced in the body (e.g. model ID) doesn’t exist. Legacy: [legacy:http_404]. | Verify model IDs. |
request_too_large | The line body exceeded the per-request size or token budget. Legacy: [legacy:http_413]. | Reduce input length. |
insufficient_quota | Organization quota ran out partway through the batch. Legacy: [legacy:http_420] / [legacy:http_429]. | Top up balance and re-submit the failed lines. |
rate_limit_exceeded | The endpoint throttled the line (non-quota rate limit). | Re-submit the line; transient. |
internal_error | The upstream service returned 5xx on every retry, or the queue exhausted delivery attempts. Legacy: [legacy:http_5xx] / [legacy:retries_exhausted] / [legacy:dlq_exhausted]. | Re-submit the line; transient. |
batch_cancelled | The parent batch was cancelled via POST /v1/batches/{id}/cancel before this line was dispatched. | The line did not run; submit a fresh batch if you still need the result. |
batch_expired | The 24-hour completion window elapsed before this line was dispatched. | Submit a fresh batch with the remaining lines. |
[legacy:<old_code>] prefix
on error.message for forensics. No error.details field is emitted -
the relevant upstream detail (if any) is folded into the message.
Recovering from failures
A batch that ends with some failures is stillstatus: "completed".
The pattern for handling this is:
- Read
output_file_idand write successful results to your data store. - Read
error_file_idand partition byerror.code:invalid_request_error,not_found_error,request_too_large: client-side issues, log, fix the input lines, and resubmit.internal_error,rate_limit_exceeded: transient, resubmit as-is.insufficient_quota,authentication_error: operational, fix the underlying issue first, then resubmit.batch_cancelled,batch_expired: the lines never ran; resubmit them in a fresh batch.
- Build a smaller JSONL containing only the lines you want to retry, upload, and create a new batch.
custom_id is preserved end-to-end, retries are easy to deduplicate
into your existing data store.
When to contact support
Reach out to ZeroGPU support if you see:- Persistent
500responses from any endpoint. Input file object missingon a file you just uploaded.- A batch stuck in
finalizingfor more than 10 minutes. - A batch that completes but is missing both
output_file_idanderror_file_idwhilerequest_counts.totalis non-zero.
Next steps
Objects & lifecycle →
The Batch object, status lifecycle, and every endpoint.
JSONL format →
Line schemas for input, output, and error files.
Quickstart →
First batch in under 10 minutes, auth, upload, create, download.

