batchwatch

batchwatch › batchwatch API reference

batchwatch API reference

Base URL: https://batchwatch.dev (https://api.batchwatch.dev and https://www.batchwatch.dev are the same worker — no redirect).

Base URL: https://batchwatch.dev (https://api.batchwatch.dev and https://www.batchwatch.dev are the same worker — no redirect).

Everything below was written by reading src/index.js and then calling the routes. Every example response in these documents is a real captured response, and each one says where it came from. Nothing here is invented.

Provenance of the examples

Two sources, and they are always labelled:

LabelWhat it means
Captured from https://batchwatch.devA real call against production on 2026-08-25. The numbers are whatever the live dataset held at that moment.
Captured from a local wrangler dev instanceThe same code, run locally against a local D1 seeded with synthetic measurements. Used only for routes that cannot be reached from an unauthenticated caller, or for response shapes production could not produce at the time. The numbers in those examples are not real queue times.

Why some examples are local: creating an API key from the machine used for this work returned 429 too many keys from this address today, so the key-authenticated routes could not be exercised against production. Writing measurements into production to demonstrate the ingest routes was deliberately not done — the dataset is the product, and it must not contain data that was made up to illustrate documentation.

Routes

MethodPathKey requiredDelayed by tier
GET/healthnon/a
GET/v1/statusnono
GET/v1/probenono
GET/v1/outagesnono, never
GET/v1/outages.atomnono, never
GET/v1/coveragenono
GET/v1/waitnoyes
GET/v1/recommendnoyes
GET/v1/curvenoyes
GET/v1/should-i-batchcontribution or free trialyes
GET/v1/b2b/should-i-batchcontribution or free trialyes
GET/v1/estimate-batchtimecontribution or free trialyes
GET/v1/conditionscontribution or free trialyes
GET/v1/distributioncontribution or free trialyes
POST/v1/subscriptionsyesn/a
GET/v1/subscriptionsyesn/a
DELETE/v1/subscriptions/{id}yesn/a
POST/v1/keysnon/a
GET/v1/keys/currentyesn/a
DELETE/v1/keys/currentyesn/a
POST/v1/callsno (optional)n/a
PATCH/v1/calls/{id}no (optional)n/a
POST/v1/calls/completeyesn/a
GET/v1/calls/mineyesn/a
DELETE/v1/calls/mineyesn/a
POST/v1/rollup/refreshyesn/a
OPTIONSanynon/a

Plus the non-API pages served by the same worker: / and /index.html (dashboard, HTML), /m/{provider}/{model} and /p/{provider} (public model and provider pages, HTML), /badge/{provider}/{model}.svg (an embeddable live status badge — see Status badges below), /robots.txt, /sitemap.xml. See ops.md.

Routes that do not exist, despite being named in docs/KRAVSPEC.md: /v1/batches, /v1/batches/complete, /v1/forecast, /v1/tradeoff, /v1/anomalies, /v1/status.atom. All six return 404 (verified against production on 2026-08-25; /v1/coverage returned 200 in the same run as a positive control). KRAVSPEC is out of date; this reference describes the code.

Authentication

Authorization: Bearer bw_...

The token is hashed with SHA-256 and compared against api_key.token_hash. A revoked key (revoked_at set) does not authenticate.

An absent or unrecognised header is not an error on most routes — the caller is simply treated as anonymous. Only the routes marked "key required" above return 401.

Get a key with POST /v1/keys. No email, no confirmation.

Path and method handling

CORS

All JSON responses carry access-control-allow-origin: * and cache-control: no-store. OPTIONS on any path returns 204 with:

access-control-allow-origin: *
access-control-allow-methods: GET,POST,PATCH,OPTIONS
access-control-allow-headers: authorization,content-type

Note that DELETE is not in access-control-allow-methods, although DELETE /v1/calls/mine and DELETE /v1/keys/current exist. Browser preflight for those two routes will therefore fail; call them server-side. (Verified against production 2026-08-25 — see the discrepancy list at the bottom of ops.md.)

Status badges

Drop a live batchwatch median into any README, blog or status page. It is a plain SVG you <img>-embed, it updates itself, and it links back to the model's page. The number is the same measured p50 median /v1/coverage and /v1/wait report — never invented.

https://batchwatch.dev/badge/{provider}/{model}.svg

Copy-paste, Markdown:

[![OpenAI gpt-5-nano batch queue time](https://batchwatch.dev/badge/openai/gpt-5-nano.svg)](https://batchwatch.dev/m/openai/gpt-5-nano)

Copy-paste, HTML:

<a href="https://batchwatch.dev/m/openai/gpt-5-nano">
  <img src="https://batchwatch.dev/badge/openai/gpt-5-nano.svg"
       alt="OpenAI gpt-5-nano batch queue time">
</a>

The provider and model are separate path segments — the model name keeps every hyphen and dot it has (gpt-5.6-sol, claude-haiku-4-5, gemini-3.7-flash). Use the same provider/model you would pass to /v1/wait.

The badge draws one of three states, and they look different on purpose:

A shorter /badge/{model}.svg form also works when the model name is unique across providers; otherwise use the {provider}/{model} form so it is unambiguous.

Let an agent call these routes for you — the MCP server

If your caller is an AI agent (Claude, Cursor, Cline, or anything that speaks MCP), it does not have to scrape a page about batchwatch — it can call batchwatch directly. The batchwatch MCP server (batchwatch-mcp, in the mcp/ folder of the client repo) wraps these read routes as six MCP tools: should_i_batch, estimate_batchtime, conditions, distribution, coverage and wait. When a model is deciding "batch this or run it synchronously?", it invokes the tool and gets the real measured queue evidence — the wait distribution, the current conditions, a graded confidence and how fresh the data is — passed through verbatim, never a fabricated number.

It runs on the same tier system as the rest of the API: without a key the tools read the public data delayed 15 minutes; a key earns that key's live figures, which is exactly what an agent needs to route real traffic. See clients for how to wire it into an MCP client. (Today it runs from source with node mcp/index.js; publication to npm as batchwatch-mcp is pending.)

The rest of this reference

Quickstart

# open, no key, delayed 15 minutes
curl 'https://batchwatch.dev/v1/wait?provider=openai&model=gpt-5-nano'

# get a key
curl -X POST https://batchwatch.dev/v1/keys \
     -H 'content-type: application/json' \
     -d '{"label":"prod-pipeline"}'

# contribute: start a measurement when your batch job starts
curl -X POST https://batchwatch.dev/v1/calls \
     -H "authorization: Bearer $BW_KEY" -H 'content-type: application/json' \
     -d '{"provider":"openai","model":"gpt-5-nano","mode":"batch","requests":1200,"input_tokens":840000}'

# ...and close it when the job finishes
curl -X PATCH https://batchwatch.dev/v1/calls/c_xxxxxxxxxxxxxxxxxxxx \
     -H "authorization: Bearer $BW_KEY" -H 'content-type: application/json' \
     -d '{"status":"completed","output_tokens":250000}'

Five completed measurements in the last seven days makes you a contributor, which unlocks the four gated routes and cuts the delay from 900s to 300s.

Reference

Measurement routes

Nine routes answer questions about queue time. Five are open to anyone (/v1/wait, /v1/recommend, /v1/curve, /v1/coverage, /v1/accuracy); four are gated…

Contributing measurements

There are two ways to get a measurement into the dataset, and they are not equally trusted.

Keys

A key is self-service: one request, no password, and no email needed to get started. You get a key by asking for one — and everything the free and…

Tiers, delay, quota and the free trial

Two things are sold separately, and the split is the whole design:

Error model

Every error is JSON with an error field holding a human-readable message, plus route-specific fields. There is no error code enumeration — the status code…

Reading an answer

Most responses carry the same four disclosure fields: confidence, basis, n and freshness. They exist because the alternative — a single number with no…

Operational and public routes

Health, self-check, prober state, outage feeds, the forced rollup, and the HTML pages the same worker serves.

Outage alerts

Batchwatch watches every model it measures and opens an outage when the queue for one of them degrades against its own baseline. This page is how you get…