Endpoints
Anthropic Messages API
An Anthropic-compatible /v1/messages endpoint so the Anthropic SDKs and Claude Code work out of the box.
Pendra translates Anthropic's Messages format to OpenAI Chat Completions under the hood, so you can point any Anthropic SDK — or Claude Code — at Pendra and get Anthropic-shaped responses back. The Python and JavaScript examples use the official Anthropic SDKs; only the base URL and key change.
Authentication
Send your Pendra API key on the x-api-key header (Anthropic
convention). The Authorization: Bearer header also works for
OpenAI-style clients.
model
string
required
404.
max_tokens
integer
required
messages
array
required
{ role, content } objects.
system
string | array
stream
boolean
default: false
true for Anthropic-format SSE (see Streaming below).
tools
array
tool_choice.
Other optional fields
temperature,top_p,top_k,stop_sequences— standard sampling controls.thinking— pass{"type":"enabled"}to request a chain-of-thought on reasoning-capable models, or{"type":"disabled"}to skip thinking on hybrid models that support toggling it. See Thinking.cache_control— accepted and ignored. You don't need to place cache breakpoints on Pendra; see Prompt caching below.
Response
Pendra returns the Anthropic message envelope (see example).
content is an array of content blocks (text, tool_use, etc.).
stop_reason is "end_turn" on a natural finish or
"max_tokens" when capped. usage uses Anthropic
naming (input_tokens / output_tokens).
Prompt caching
Pendra caches prompts automatically. When you send follow-up turns of the same conversation, it reuses the part of the prompt it already processed instead of reprocessing it from scratch, which makes the reply start sooner. You don't have to do anything to switch this on.
usage.cache_read_input_tokens tells you how many of that
request's input_tokens came from the cache, so a cache hit rate
is cache_read_input_tokens / input_tokens. It's always present —
0 on a first, cold request. A higher rate means a faster first
token on that request.
cache_creation_input_tokens is always 0. Other
providers charge for writing a prompt into the cache and report it here;
Pendra's caching is automatic and free, so there is nothing to report.
Because it's automatic, Pendra doesn't use cache breakpoints. If your
client sends cache_control blocks — Claude Code and the
Anthropic SDKs do — Pendra accepts the request and ignores the field. You
can leave your breakpoints in place; they simply have no effect, and
removing them changes nothing. Tuning them won't move your hit rate either:
cache_read_input_tokens is the number to watch, and it reflects
what Pendra reused on its own.
On a streamed response, the cache numbers arrive on the final
message_delta event rather than message_start —
they aren't known until the model has finished reading the prompt.
Streaming
Set "stream": true to receive Anthropic-format SSE events
(message_start, content_block_delta,
message_delta, message_stop).
event: message_start
data: {"type":"message_start","message":{"id":"msg_01N3Xc8","type":"message","role":"assistant","content":[],"model":"qwen3.6:27b","stop_reason":null,"usage":{"input_tokens":9,"output_tokens":0}}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":9,"output_tokens":2,"cache_creation_input_tokens":0,"cache_read_input_tokens":0}}
event: message_stop
data: {"type":"message_stop"}
While the model is still reading a long prompt, Pendra sends an SSE comment
line (: keep-alive) every 15 seconds so the connection
isn't dropped as idle before message_start arrives. It carries
no event: and no data:, so the Anthropic SDKs and
Claude Code discard it; only hand-rolled parsers need to skip lines starting
with :.
If you pin a worker with X-Pendra-Worker-Id and it can't serve
the request — it doesn't exist, doesn't have the model, or is running a
version too old for this endpoint — you get a normal HTTP error response
instead of a stream, so check the status code before you start reading
events. A worker that's merely busy, or not connected yet, still reports
in-band as an error event once the stream has started. See
Errors & rate limits.
Pin a Pendra model
Claude Code defaults to Anthropic's own model names, like
claude-sonnet-4-5. Pendra serves open models under their own
ids and won't substitute one for a name it doesn't recognise, so those
requests come back as a 404 telling you which variable to set.
Pick an id from /models and set it before you
start.
Use with Claude Code
Point Claude Code at Pendra by setting four environment variables — see the full recipe in Integrations → Claude Code.