Skip to main content

Overview

The Batch API runs many Agent API requests asynchronously. You upload a JSONL file of requests, create a batch from it, poll the batch, and download an output file when it finishes. Use batch for work that does not need an immediate answer, such as evaluations, classification, enrichment, or offline research over a large dataset. Each request runs the full Agent API loop, so built-in tools such as web_search and fetch_url work inside a batch. Requests that run inside a batch do not count toward your Agent API request rate limit. Token rate limits still apply, and the batch retries a request that hits one. The Batch and Files endpoints follow the shape of OpenAI’s Batch API.
The Batch API is available to every API organization, except organizations with zero data retention. Batch keeps inputs and outputs for 30 days.
The examples on this page read your API key from the PERPLEXITY_API_KEY environment variable. The Python and TypeScript examples use client.files and client.batches in the Perplexity SDKs. The cURL examples use jq.
Batch files at /v1/files are separate from the files an Agent API response can create, which are covered in Working with files.

Run a batch

1

Write the input file

Create a JSONL file: one JSON object per line, with no surrounding array, no commas between lines, and no blank lines. Each line is one request.
batch_input.jsonl
Save the file as batch_input.jsonl.
2

Upload the file

Upload the file to POST /v1/files with purpose=batch, and save the returned file id. The API validates every line before it stores the file.
Response
If a line is invalid, the upload returns 400 with the first invalid line’s number and the reason, and nothing is stored:
A successful upload checks the file format. A request can still fail when it runs, for example if the model id does not exist. Those requests go to the error file.Files are kept for 30 days. Custom expiry is not supported, so sending expires_after returns 400.
3

Create the batch

Create a batch from the file id, and save the returned batch id. endpoint is required and must be /v1/responses.
Response (abbreviated)
output_expires_after is not supported and returns 400. The batch’s expires_at is its deadline, 48 hours after created_at. See Timing and expiry.The Idempotency-Key header is optional and makes retries safe. In the SDKs, pass it as idempotency_key in Python, or as { 'Idempotency-Key': key } in the second argument in TypeScript. Use a new key for each new batch, such as a UUID. If you retry the same create with the same key within 24 hours, the API returns the original batch instead of creating another. Reusing a key with a different input_file_id, endpoint, or metadata returns 400. Keys are up to 200 bytes.
4

Poll the batch

Poll GET /v1/batches/{batch_id} until the status is completed, expired, cancelled, or failed. Polling every 30 to 60 seconds is enough for most batches.
The batch and file endpoints use your organization’s request rate limit for its usage tier. At tier 0 that is 1 request per second. Do not poll faster than that. If you get 429 while polling, poll less often.
Response
If the response has no status, it is an API error. The loop prints it and stops. usage sums the token usage of the requests in the output file only, so it is not your full bill.
5

Download the results

A final batch status means processing has ended. It does not mean every request succeeded.
  • output_file_id is the file of requests that ran to the end. It can point to an empty file.
  • error_file_id is the file of requests that did not, or null when there are none.
Download each file with GET /v1/files/{file_id}/content, then check each line’s response.body.status.
Output

Supported requests

Batch accepts any model from the Agent API models list. Every line of a file must use the same model. Requests that use an anthropic/* model must include max_output_tokens, as in a normal request. A line without it fails with 400 in the error file. Other Agent API fields work as they do in a normal request, including instructions, max_steps, max_output_tokens, reasoning, and response_format. The file must be UTF-8. Lines can end in LF or CRLF. Empty lines and duplicate JSON keys are rejected.

Output file format

Each output line has the request’s custom_id and the full Response object in response.body. Each request is a normal Response, so you can also fetch it with GET /v1/agent/{id} using response.body.id. The example below is abbreviated.
  • Do not rely on line order. Match results to requests by custom_id.
  • response.body.status can be completed, incomplete (for example when max_output_tokens is reached), or failed. Check it before you use the output.

Error file format

Requests that did not produce a result go to the error file and count as failed. A line has one of two shapes. A request that the API rejected, or that failed before it produced any output, has error: null. response holds the status code and error body, in the same error format as a normal request. For example, an input over the model’s context window gets 400.
A request that failed after it produced output stays in the output file, with response.body.status set to failed. A request that stopped without an answer has response: null and an error:

Batch statuses

Once the batch reaches a final status, request_counts.completed is the number of lines in the output file. request_counts.failed is the number of lines in the error file, including expired and cancelled requests.

Timing and expiry

Batches target completion within 24 hours, the completion_window. That is a target, not a guarantee. The batch’s expires_at is the deadline, 48 hours after the batch was created. At the deadline, requests that have not started, or are waiting for capacity, expire with batch_expired. Expired requests are not billed. Requests that are already running continue until they finish, and they bill normally. When the last running request finishes, the batch ends expired if any request expired. Otherwise it ends completed.

Cancel a batch

Cancel a batch that is validating or in_progress with POST /v1/batches/{batch_id}/cancel.
Response
The batch moves to cancelling, then to cancelled once running requests stop. Poll the batch to see the final status.
  • Requests that completed before the cancel stay in the output file and are billed.
  • Requests already running can still finish for a short time after the cancel, usually up to about a minute. Those that finish go to the output file and are billed.
  • Running requests that are stopped go to the error file with batch_cancelled. They can bill for the steps they already ran.
  • Requests that had not started go to the error file with batch_cancelled. They are not billed.
Cancelling a batch that is already cancelling or cancelled returns it unchanged. Cancelling a batch that is finalizing, completed, expired, or failed returns 409.

List batches

GET /v1/batches lists your organization’s batches, newest first. limit is 1 to 100 and defaults to 20. If has_more is true, pass the page’s last_id as after to get the next page.
Response

Files and retention

Input, output, and error files expire 30 days after they are created, as shown in each file’s expires_at. Download your results before then.
  • GET /v1/files lists your organization’s files, newest first. It includes your uploads and the output and error files that batches write. Deleted and expired files are not listed.
  • GET /v1/files/{file_id} returns a file’s metadata.
  • GET /v1/files/{file_id}/content downloads its contents.
  • DELETE /v1/files/{file_id} deletes it.
Deleting an input file does not affect batches already created from it. Output and error files have purpose set to batch_output. GET /v1/files takes the same limit and after paging as the batch list. limit is 1 to 100, which is lower than OpenAI’s maximum. Set order to asc for oldest first, and purpose to batch or batch_output to filter.
cURL
Response

Limits

Your organization can also have a limited number of unfinished batch requests at once, based on its usage tier. Each line of an input file is one request. The count drops as requests finish. A create that would go over the limit returns 429 with the current count and limit. Wait for earlier batches to finish, or submit a smaller file.

Billing

There is no separate batch price. OpenAI models run on the flex service tier where the model lists it. Other models run on the standard tier at their normal price. Starting 20 to 24 hours after the batch was created, requests that start or resume run on the standard tier so they can finish before the deadline. Each result’s response.body.service_tier shows the tier that served it. Tokens bill at the tier that served each step. Tool calls bill at their normal price. Requests that never started or that expired are not billed. A request stopped while it was running can bill for the steps it already ran. Creating a batch requires API credits. If your credits run out while a batch runs, the batch pauses. Requests refused for credits do not fail. The batch resumes after you add credits. The batch deadline does not change while it is paused. Requests still waiting at the deadline expire with batch_expired.

Errors

Next steps

Background mode

Models

Create batch reference

Upload file reference