Files and batches
The OpenAI shapes for a file and a batch, with the fields OpenFill adds. A file exists to be a batch input or a batch output; there is no other purpose.
Set shell variables such as ORDER_ID, FILE_ID and BATCH_ID to ids returned by the API before running the corresponding command.
Upload a file
curl https://api.openfill.ai/v1/files -H "Authorization: Bearer $OPENFILL_API_KEY" \ -F purpose=batch -F [email protected]Multipart, one file, purpose of batch. The content is checked when a batch starts rather than at upload. A file over 50 MB is refused with 413.
{"id": "file-...", "object": "file", "bytes": 1234, "created_at": 1756700000, "expires_at": 1756786400, "filename": "requests.jsonl", "purpose": "batch", "status": "processed"}expires_at is the retrieval deadline, derived from the account setting and batch.min_retention_days on GET /v1/rate-limits. The file can be deleted early or evicted under the storage cap. Both timestamps are epoch seconds.
Get a file and its content
curl "https://api.openfill.ai/v1/files/$FILE_ID" -H "Authorization: Bearer $OPENFILL_API_KEY"curl "https://api.openfill.ai/v1/files/$FILE_ID/content" -H "Authorization: Bearer $OPENFILL_API_KEY" -o output.jsonlThe content is served as application/jsonl with a filename in Content-Disposition. A file past its expiry answers 410 result_unavailable while its metadata remains; after deletion of the file record it answers 404. A temporary read failure answers 503with Retry-After. Each download counts against the hourly allowance on rate limits.
List and delete files
curl "https://api.openfill.ai/v1/files?purpose=batch" -H "Authorization: Bearer $OPENFILL_API_KEY"Newest first, optionally filtered by purpose (batch, batch_output or batch_errors). Pass last_id as after to continue; an unknown after id restarts the list. The response carries object, data, first_id, last_id and has_more. A full page has has_more true, so the final continuation can be empty. The limit bounds are in GET /openapi.json.
curl -X DELETE "https://api.openfill.ai/v1/files/$FILE_ID" -H "Authorization: Bearer $OPENFILL_API_KEY"Set FILE_ID to an id returned by the API. Delete returns id, object: file and deleted: true. A repeated delete answers 404. A file used by an active batch answers 409 file_in_use; cancel the batch and wait for it to finish, or wait for normal completion.
Create a batch
curl https://api.openfill.ai/v1/batches -H "Authorization: Bearer $OPENFILL_API_KEY" -H "Content-Type: application/json" \ -d '{"input_file_id": "'"$FILE_ID"'", "endpoint": "/v1/chat/completions", "completion_window": "24h", "price_limit": 0.02}'| Field | Type | Default | Meaning |
|---|---|---|---|
| input_file_id | string | required | A file uploaded with purpose batch. |
| endpoint | string | required | /v1/chat/completions, /v1/completions, /v1/responses or /v1/messages. Every line's url must match it, and a line's body and output take that endpoint's shape. |
| completion_window | string | 24h | Only 24h is accepted, as OpenAI's shape has it. |
| price_limit | number | key or account | The price limit every line on a market model inherits. A line's own body.price_limit wins. When omitted, each line resolves its model's key or account default. A line on a fixed-price model ignores it. |
| max_wait | integer | a day | The deadline in seconds, clamped to the configured order timeout range. Lines still queued at expires_at expire unexecuted. |
| metadata | object | none | Up to 16 pairs; keys up to 64 characters, values up to 512. |
Create answers 200 with status validating. Each line resolves its own price; a missing price rejects a line on a market model as invalid_request, and a line on a fixed-price model needs none. The batch records every model's pricing when it is created, and a line whose model's pricing changed since is not run; it lands in the error file under prices_changed. The batch price_limit accepts 0 to 1000000000, and max_wait accepts positive integers up to 2592000. Too many active batches answers 429 withRetry-After. Up to 10,000 lines per file.
An input file past its retention deadline answers 410 result_unavailable before the batch is created.
The batch object
| Field | Meaning |
|---|---|
| id | batch_ and 24 characters. |
| endpoint | /v1/chat/completions, /v1/completions, /v1/responses or /v1/messages. |
| status | validating, in_progress, cancelling, completed, expired, cancelled or failed. |
| input_file_id, output_file_id, error_file_id | The files. Output and error are null until the batch finishes, and stay null when that file would have been empty. |
| errors | null, or {object: list, data: [{code: batch_error, message}]} when validation failed. |
| completion_window | Always 24h. |
| created_at, expires_at, completed_at | Epoch seconds. expires_at is the real deadline. |
| request_counts | {total, completed, failed}. failed folds in expired and cancelled lines. |
| metadata | Up to 16 string pairs, as sent. |
| x_market | {active, pending_result, expired, canceled, bid}. bid is the batch price_limit, or null. |
| x_retention | {output_days, output_expires_at, output_deleted} for the output file. |
List, retrieve and cancel
curl "https://api.openfill.ai/v1/batches?limit=20" -H "Authorization: Bearer $OPENFILL_API_KEY"{"object": "list", "data": [ ...batches... ], "first_id": "batch_...", "last_id": "batch_...", "has_more": true}Newest first. Pass last_id as ?after= for the next page. has_more is true whenever a page is full, so the page after the last one is empty with has_more false. The limit defaults to the example above; its maximum is described by GET /openapi.json. An unknown after id restarts the list.
curl "https://api.openfill.ai/v1/batches/$BATCH_ID" -H "Authorization: Bearer $OPENFILL_API_KEY"curl -X POST "https://api.openfill.ai/v1/batches/$BATCH_ID/cancel" -H "Authorization: Bearer $OPENFILL_API_KEY"Cancel answers the batch with status cancelling; queued lines are cancelled and executing ones finish. Only validating and in_progress can be cancelled. A batch in any other state answers 409 batch_not_cancelable.
Line formats
{"custom_id": "job-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "market-preview", "messages": [{"role": "user", "content": "hi"}], "price_limit": 0.03}}{"id": "batch_req_...", "custom_id": "job-1", "response": {"status_code": 200, "request_id": "<order_id>", "body": { ...the completion... }}, "error": null}{"id": "batch_req_...", "custom_id": "job-2", "response": null, "error": {"code": "expired", "message": "..."}}The error codes, the statuses, and what a line cannot carry are on the batch page.