Streaming, Errors & Resilience

Ping Events: Why SSE Pings Exist and Why Intermediate Proxies Must Not Drop Them

Streams don't only carry answers — they carry heartbeats. The unglamorous ping event is what keeps a long Claude stream alive through the corporate proxies, load balancers, and idle timeouts that sit between the API and your code.

Claude 3P 101 · Updated July 2026 · Unofficial guide

When you stream a Claude response, you receive server-sent events (SSE): a long-lived HTTP response carrying a sequence of small, named events — message_start, content_block_delta and friends, ending in message_stop. Scattered among them you will also see events that carry no content at all: ping events, which Anthropic's streaming documentation says may appear anywhere in the stream. They aren't noise or a bug. They are the connection's heartbeat, and infrastructure that mishandles them is one of the quieter causes of "our streams die randomly on long requests."

Why streams go quiet in the first place

It's tempting to picture a stream as a steady drip of tokens, but real Claude streams have documented silent stretches. Tool-use input arrives as input_json_delta fragments, and because current models emit one complete key/value pair at a time, there may be delays between events while a large parameter value is assembled. A thinking block streamed with display: "omitted" produces no visible deltas at all — the block opens, eventually receives a single signature_delta, and closes, which can mean a long pause before the first text token. During any of these gaps, nothing application-visible needs to be sent — but a totally silent TCP connection is exactly what network middleware is configured to kill.

Every hop between Anthropic and your process — cloud load balancers, API gateways, corporate forward proxies, NAT devices — typically enforces an idle timeout: if no bytes flow for some interval, the connection is presumed dead and closed. Ping events solve this by ensuring bytes do flow. Each ping resets every idle timer along the path. Anthropic's SDKs add a second layer of the same defense at the transport level by enabling TCP keep-alive, but application-level pings are the mechanism that intermediaries which only look at the HTTP layer actually see.

The two ways infrastructure breaks this

Buffering. Some proxies and gateways buffer responses — collecting the body to compress, scan, or batch it before forwarding. Buffering an SSE response is fatal twice over: events (pings included) sit in the buffer instead of reaching the client, so downstream idle timers fire even though the origin is healthy, and your application's time-to-first-token balloons. Any component between Claude and your client must forward streaming responses unbuffered, as they arrive.

Filtering. If you build your own relay — a backend that consumes the Claude stream and re-emits events to browsers — it's tempting to forward only the "useful" events and drop pings. Do that and you've re-created the problem one hop downstream: your relay's outbound connections now go silent during the model's quiet stretches, and your load balancer starts killing them. Pass pings through, or emit your own heartbeat on the client-facing leg.

Rule of thumb: a streaming pipeline is only as patient as its least patient hop. Inventory every intermediary between the Claude endpoint and the end consumer, confirm each one passes SSE through unbuffered, and treat unexplained stream deaths at suspiciously round durations (60s, 120s, 300s) as an idle-timeout hunt, not an API bug.

Handling pings in application code

In your own consumer, pings require exactly one behavior: don't crash on them. Code that switches on event type and assumes every event is content will hit ping events wherever they appear. Handle the types you care about, tolerate the rest, and let the SDK's event iterators (which already do this) insulate you where possible. What pings should not do is feed your progress logic — "we're receiving pings" means the connection is alive, not that generation is advancing. Progress signals come from deltas; liveness comes from pings; completion comes only from message_stop, as discussed in mid-stream error events.

On the 3P platforms

Streaming is available on all four third-party platforms, and the SSE anatomy described here is Anthropic's documented format, served natively by the Claude API, Claude Platform on AWS, and the current "Claude in Amazon Bedrock" surface. Bedrock's legacy InvokeModelWithResponseStream wraps chunks in AWS's own event-stream encoding instead, and each cloud fronts its endpoint with its own networking layer — so when a stream dies on a 3P platform, extend the timeout hunt to that platform's gateways and your own VPC path as well.

Where to go next

Start with SSE streaming in the SDKs for the consumer side, streaming event types for the full vocabulary, and streaming UX and timeouts for the product-level view of long waits.

Sources