Skip to content
Batchwork
Esc
navigateopen⌘Jpreview

Batch APIs for Python

Save up to 50% on AI batch requests.

Use OpenAI, Anthropic, Gemini, Groq, Mistral, Together AI, and xAI through the same async Python API, or from your terminal with the batchwork CLI. Built for people, scripts, and coding agents alike.

OpenAIAnthropicGoogle GeminiGroqMistralTogether AIxAI
add batchwork-ai
import asyncio

from batchwork import BatchRequest, Batchwork


async def main() -> None:
    requests = [
        BatchRequest(custom_id="doc-1", prompt="Summarize the first document"),
        BatchRequest(custom_id="doc-2", prompt="Summarize the second document"),
    ]

    async with Batchwork() as client:
        job = await client.batch(
            model="openai/gpt-5.6-sol",
            requests=requests,
        )
        await job.wait(timeout=3600)

        async for result in job.results():
            print(result.custom_id, result.status, result.text)


asyncio.run(main())

Providers set batch pricing and model eligibility. Check current pricing before submitting.

What Batchwork handles

One Python API for every provider.

Submit requests and read results the same way across OpenAI, Anthropic, Gemini, and the rest. Batchwork handles each provider's files, endpoints, and output formats.

01

The same job API

Submit, poll, wait, cancel, resume, and collect results the same way for every provider.

02

Native batch endpoints

Requests still go to each provider's own batch API and use its batch pricing.

03

Results keyed by custom_id

Handle unordered successes, errors, usage, embeddings, and images without provider-specific parsing.

Switch providers

Change the model. Keep everything else.

The same requests, job lifecycle, and result handling work across provider batch APIs.

OpenAI

job = await client.batch(    model="openai/gpt-5.6-sol",    requests=requests,) await job.wait() async for result in job.results():    print(result.custom_id, result.text)

Anthropic

job = await client.batch(    model="anthropic/claude-sonnet-4-6",    requests=requests,) await job.wait() async for result in job.results():    print(result.custom_id, result.text)

Submit

Submit a batch with normal Python objects.

Choose a provider and model, pass your requests, and get a BatchJob back as soon as the provider accepts the batch.

Read the guide
from batchwork import BatchRequest, Batchwork

requests = [
    BatchRequest(
        custom_id=document.id,
        prompt=f"Summarize:\n{document.text}",
        max_output_tokens=400,
    )
    for document in documents
]

async with Batchwork() as client:
    job = await client.batch(
        model="anthropic/claude-sonnet-4-5",
        requests=requests,
    )

print(job.id, job.provider, job.status)

Results

Process every result by custom_id.

Provider output may be unordered and can contain both successes and failures. Batchwork gives every item the same status and result shape.

Read the guide
await job.wait(timeout=3600)

async for result in job.results():
    match result.status:
        case "succeeded":
            await save_summary(
                result.custom_id,
                result.text,
                result.usage,
            )
        case "errored":
            await queue_retry(
                result.custom_id,
                result.error,
            )
        case "expired" | "canceled":
            await mark_unfinished(result.custom_id)

Terminal and agents

Run the same batches without writing Python.

The batchwork CLI ships in the same package. You get readable summaries in the terminal; your scripts and coding agents get schema-versioned JSON, stable exit codes, and a local registry for resuming jobs. An Agent Skill teaches agents to drive it safely.

Read the guide
uv tool install batchwork-ai

# Submit a file of prompts and check on it later
batchwork submit text prompts.txt --model openai/gpt-5
batchwork wait BW_RECORD_ID --timeout 2h
batchwork results BW_RECORD_ID

# Machine mode for scripts and coding agents
batchwork --jsonl --quiet run text requests.jsonl \
  --model openai/gpt-5

Production

Poll jobs outside your request handler.

Store tracked jobs, run the poller from a worker or cron, and send a signed webhook when a batch finishes. OpenAI's native webhooks use the same completion path.

Read the guide
from batchwork import (
    BatchPoller,
    ProviderCredentials,
    TrackTarget,
    create_memory_store,
)

poller = BatchPoller(
    create_memory_store(),
    credentials=ProviderCredentials(api_key="..."),
)

target = TrackTarget(
    id=job.id,
    provider=job.provider,
    status=job.status,
)
await poller.track(
    target,
    webhook_url="https://app.example/webhooks/batch",
    secret=webhook_secret,
)

# Run from a worker or scheduled task.
result = await poller.tick()

Provider support

Text on all seven providers. Embeddings and images where supported.

Input media and generated output are listed separately. A provider can accept images in a text request without supporting image generation.

When you need more

Add storage and webhooks without changing the batch API.

Start with a script. Add persistence, media handling, scheduled polling, or signed delivery when the workload moves into production.

Browse the public API
01

Media inputs

Use images, PDFs, text files, audio, and provider file references where the selected provider supports them.

02

Three output types

Run text batches on all seven providers, embeddings on three, and image generation on OpenAI, Google, and xAI.

03

Persistent stores

Track jobs in memory during development or use Redis-compatible storage in production.

04

Signed webhooks

Sign completion events, reject stale deliveries, and deduplicate retries by event ID.

05

Provider options

Pass provider-specific settings without changing the shared request and result models.

06

No provider SDKs

Batchwork talks to provider APIs directly with HTTPX.

Get started

Install Batchwork and submit a batch.

Add one provider credential, choose a model, and use the same job API for every supported provider.