API reference

Every request carries the caller's own provider API key. Two different callers' identical requests never share a cache entry — the cache key is owner-scoped, not just content-addressed.

Chat completions

POST /{provider}/v1/chat/completions
Authorization: Bearer <caller's own key for that provider>

{ "model": "...", "messages": [...], "temperature": 0 }   # OpenAI chat shape, forwarded verbatim on a miss

→ the upstream response body, unchanged      # 200 on a cache hit, ~1 ms
  X-Zerocache-Completion-Hit: true | false
  X-Zerocache-Completion-Hit-Kind: exact | semantic     # on a hit
  X-Zerocache-Semantic-Score: 0.982                      # on a semantic hit

Only deterministic requests are cached: temperature: 0 or an explicit seed, with n absent or 1. Anything else is a transparent passthrough — forwarded, nothing stored, nothing counted. A non-2xx upstream response is forwarded with its real status and never cached. The cache key is the canonicalized body: blind to user / stream / metadata / key order / number spelling.

stream: true works — the SSE is buffered on a miss (streamed to the caller live meanwhile) and replayed frame-by-frame on a hit. One entry serves both streaming and non-streaming callers.

{provider} is any of openai, mistral,gemini, groq, deepseek, together,openrouter, xai, fireworks, plus anything added viaZEROCACHE_CHAT_PROVIDERS.

Anthropic Messages

POST /{provider}/v1/messages
Authorization: Bearer <caller's own Anthropic key>       # rewritten to x-api-key upstream
anthropic-version: 2023-06-01                             # optional; folded into the cache key

{ "model": "...", "max_tokens": 512, "messages": [...], "temperature": 0 }

→ the upstream response body, unchanged
  X-Zerocache-Completion-Hit: true | false
  X-Zerocache-Completion-Hit-Kind: exact

Claude's native /v1/messages shape. Cache gate is temperature: 0only (Anthropic has no seed / n), plus non-emptymessages and thinking absent or disabled.anthropic-version (default 2023-06-01) and anthropic-betaare folded into the cache key. stream: true is a raw passthrough.

Text embeddings

POST /{provider}/v1/embeddings
Authorization: Bearer <caller's real provider API key>

{ "model": "<real upstream model name>", "input": ["text1", "text2"] }

Response:

{
  "object": "list",
  "data": [{ "embedding": [...], "index": 0 }],
  "model": "...",
  "usage": {...}
}

input accepts either a JSON array of strings or a single bare JSON string — both are valid, matching OpenAI's real input: string | string[] contract.

Cloud provider routing

Azure, Bedrock, and Vertex AI each front several model vendors behind one API, so routing coordinates (region, project, input/task type) are encoded into the model string itself — no new wire field, since model is already free-form per request and already lands in the cache key. azure is only a legal {provider}once an Azure base-URL env var is set; otherwise it 404s like any unknown provider.

{provider}model grammarexample
bedrock[<region>/]<modelId>[#<input_type>]us-east-1/cohere.embed-english-v3#search_query
vertexai[<location>/<project>/]<modelId>[#<task_type>]us-central1/my-proj/text-embedding-005#RETRIEVAL_DOCUMENT
azure[foundry:]<deployment>[#<input_type>]foundry:cohere-embed-v3-english#document

Image embeddings (Gemini only)

POST /gemini/v1/images/embeddings
Authorization: Bearer <caller's real Gemini API key>

{ "model": "gemini-embedding-2", "input": ["data:image/png;base64,<...>"] }

Each input entry is a data:<mime_type>;base64,<data> URI. No other provider implements image embeddings — they 404 with a message naming exactly what's unsupported.

Deleting cache entries

DELETE /{provider}/v1/embeddings
Authorization: Bearer <caller's real provider API key>

{ "model": "...", "input": ["text1", "text2"] }

{ "deleted": 2 }

Chat completions and messages have matching DELETE routes:

DELETE /{provider}/v1/{chat/completions,messages}
Authorization: Bearer <caller's own key>

{ "model": "...", "messages": [...] }

{ "deleted": 1 }

The count reflects how many keys were requested for deletion, not how many actually existed — deletion is idempotent and owner-scoped.

Error shapes

StatusMeaning
401Missing or malformed Authorization header
404Unknown {provider}, or a provider that doesn't support images/embeddings on this route
422Valid JSON, missing or wrong-typed required field
400JSON that isn't syntactically valid, or a malformed image data URI

Every error path returns the same { "error": "..." } JSON shape.

Response headers

Embeddings: X-Zerocache-Hits / X-Zerocache-Misseson every response. usage reflects only tokens actually billed for this request's misses — 0 for an all-hit batch, 0 for a coalesced request that piggybacked, and always 0 for Gemini, which never reports usage.

Chat & messages: X-Zerocache-Completion-Hit: true | false; on a hit, X-Zerocache-Completion-Hit-Kind: exact | semantic, and on a semantic hit, X-Zerocache-Semantic-Score.

Operational endpoints

Unauthenticated, outside the versioned API contract:

EndpointPurpose
GET /healthLiveness. Zero I/O — proves only that the process/router is up.
GET /readyReadiness. 503 on a store-level error; a cache miss on the sentinel key is healthy.
GET /metricsPrometheus text: embedding hits/misses/tokens (provider + content_type labels); completion hits/misses/tokens-saved (provider + stream labels); cross-replica coalesced counts; semantic index events.
GET /dashboardThe live savings SPA, embedded in the binary, polling /metrics.