Hire AI Developers Need An Online Store? Client Contracts & NDAs Grow Your Sales Funnel
Hire AI Developers Grow Your Sales Funnel

A2A Streaming with Server-Sent Events: Real-Time Task Updates

Updated July 2026
A2A streaming uses Server-Sent Events (SSE) over a standard HTTP connection to deliver real-time updates from a server agent to the client as a task progresses. Instead of waiting for the entire task to complete before seeing results, the client receives a continuous stream of status changes, intermediate messages, and artifact data as the server produces them. Streaming is activated by using the tasks/sendSubscribe method instead of the standard tasks/send.

Why Streaming Matters for Agent Collaboration

Without streaming, A2A operates in a synchronous request-response pattern. The client sends a task, and the server returns the complete result when processing finishes. For quick tasks that complete in a few seconds, this is fine. But many agent interactions take longer. A research agent might spend minutes gathering and synthesizing information. A data analysis agent might process large datasets for extended periods. A code generation agent might work through multiple iterations before producing final output.

During these longer interactions, the client has no visibility into what is happening. Is the server still working, or did something fail? Has it started the analysis, or is it still loading data? Is it 10% done or 90% done? Without streaming, the client can only wait and hope, or resort to polling with repeated tasks/get calls that add latency and waste resources on both sides.

Streaming solves this by keeping the connection open and pushing events to the client as they occur. The client sees the task transition from submitted to working, receives progress messages as the server works, sees partial artifacts appear as they are generated, and gets the final completed status when the work finishes. This real-time visibility makes the collaboration feel responsive even when the underlying work takes significant time.

The choice of SSE over WebSockets is deliberate. SSE is simpler to implement, runs over standard HTTP (making it compatible with existing proxies, load balancers, and CDNs), provides built-in reconnection support in most client libraries, and matches A2A's unidirectional update pattern. The server sends events to the client. If the client needs to send additional information (for example, responding to an input-required state), it makes a separate HTTP request using tasks/send. This separation of concerns keeps the protocol clean and each mechanism focused on what it does best.

How sendSubscribe Works

The tasks/sendSubscribe method accepts the same parameters as tasks/send: a task ID, a message containing the instructions, and optional metadata. The difference is entirely in the response format. Instead of returning a single JSON-RPC response with the completed task, the server responds with a stream of SSE events.

The HTTP response uses Content-Type: text/event-stream, the standard MIME type for SSE. The connection stays open, and the server writes events to the response body as they occur. Each event follows the SSE format: an event type field, a data field containing JSON, and a blank line separating events. The stream continues until the task reaches a terminal state (completed, failed, or canceled), at which point the server closes the connection.

From the server's perspective, the implementation involves processing the task normally while periodically flushing events to the open connection. Most web frameworks and HTTP server libraries support SSE natively or through minimal middleware. The A2A SDKs in Python, JavaScript, Go, and Java all provide helper classes that manage the event stream lifecycle, including properly formatting events, handling connection errors, and closing the stream cleanly when the task finishes.

From the client's perspective, consuming the stream involves opening the connection, parsing incoming events by type, and reacting to each event appropriately. Client SDKs typically expose this as an async iterator or callback interface, where each event triggers a handler function. The client processes events as they arrive and can maintain its own state representation of the task based on the stream.

Event Types

A2A defines two primary event types for streaming, each carrying different information about the task's progress.

TaskStatusUpdateEvent fires whenever the task's state changes. The event data includes the new status (submitted, working, input-required, completed, failed, canceled) and optionally a message from the server agent. The message might contain a status update ("Loading dataset from source"), a clarification request (when transitioning to input-required), or an error explanation (when transitioning to failed). Clients use these events to track the task's lifecycle and update their own state accordingly.

A typical sequence of TaskStatusUpdateEvent events for a successful task looks like: status changes to working (with an optional message like "Starting analysis"), zero or more intermediate working updates (with progress messages), and finally status changes to completed. For tasks that require client input, the sequence includes a transition to input-required, a pause while the client responds via tasks/send, and then a return to working.

TaskArtifactUpdateEvent fires when the server produces an artifact or adds new parts to an existing artifact. The event data includes the artifact's name, description, and the new parts being added. This event can fire multiple times for the same artifact as the server generates content incrementally. A report-generating agent might emit artifact events as it completes each section: first the introduction, then the methodology, then the findings, and finally the conclusions, each as a separate event adding to the same artifact.

Artifact events can also appear for different artifacts within the same task. A data analysis agent might produce a text summary artifact, a structured data artifact with statistical results, and a file artifact with visualizations, each emitting its own series of events as the server generates the content. The client receives all of these on the same stream and can process them independently.

Handling Streaming on the Client

Consuming an SSE stream requires parsing the event format and dispatching each event to the appropriate handler. The A2A SDKs handle the parsing automatically, but understanding the underlying mechanics helps when debugging connection issues or implementing custom behavior.

Each SSE event arrives as a block of text with two relevant fields: event: specifying the event type and data: containing the JSON payload. The data field may span multiple lines (each prefixed with data:), and events are separated by blank lines. Most SSE client libraries handle this parsing internally and deliver structured event objects to your handler code.

A well-implemented client handler does several things with the incoming events. For TaskStatusUpdateEvent with status "working," it updates any progress display and stores any messages. For TaskStatusUpdateEvent with status "input-required," it reads the server's question, determines the answer (either autonomously or by consulting its own user), and sends a response via tasks/send. For TaskStatusUpdateEvent with a terminal status, it finalizes its local task state. For TaskArtifactUpdateEvent, it accumulates artifact parts and begins processing results as they arrive.

Error handling during streaming needs special attention. The connection might drop due to network issues, proxy timeouts, or server restarts. The SSE specification includes a reconnection mechanism: the server can send an id: field with each event, and the client includes the last received ID in the Last-Event-ID header when reconnecting. This lets the server resume the stream from where it left off rather than replaying the entire event history. Not all A2A servers implement event IDs, so clients should be prepared to fall back to tasks/get to catch up on missed events after a reconnection.

Push Notifications as a Complement

Streaming and push notifications serve the same fundamental purpose, delivering real-time task updates to the client, but they work differently and suit different scenarios.

Streaming keeps an HTTP connection open for the duration of the task. This is efficient for tasks that complete within seconds or minutes, because the open connection provides the lowest possible latency for updates. But long-running tasks create problems for open connections. Load balancers and reverse proxies may enforce idle timeouts. Network infrastructure may terminate connections that stay open too long. Server resources are consumed holding connections open for tasks that might not produce events for extended periods.

Push notifications use a webhook pattern. The client registers a URL where it wants to receive updates, and the server sends HTTP POST requests to that URL when events occur. Each update is an independent HTTP request, so there is no long-lived connection to manage. This approach works well for tasks that run for minutes or hours, where maintaining an open SSE connection would be impractical or unreliable.

The client configures push notifications using tasks/pushNotification/set, providing the webhook URL and authentication credentials. The server stores this configuration and sends POST requests containing the same event types (TaskStatusUpdateEvent, TaskArtifactUpdateEvent) that would appear in an SSE stream. Each POST includes an authentication token so the client can verify that the notification genuinely came from the expected server.

Many production deployments use both mechanisms together. The client starts with streaming for immediate feedback, and if the task transitions to a long-running state, it configures push notifications as a fallback. If the SSE connection drops, the server continues delivering updates via the webhook. When the client reconnects, it can use tasks/get to synchronize its local state with the server and resume streaming if desired.

Infrastructure Considerations

Deploying A2A streaming in production requires attention to the infrastructure between client and server. Several common components can interfere with SSE connections if not configured correctly.

Reverse proxies and load balancers. Nginx, HAProxy, and cloud load balancers (AWS ALB, GCP Load Balancer) all support SSE, but some require configuration changes. Response buffering must be disabled for SSE endpoints, because buffering prevents events from reaching the client until the buffer fills. Idle timeout settings should be extended beyond the default (typically 60 seconds) to match the expected task duration. Health check intervals should not interfere with SSE connections.

HTTP/2 and HTTP/3. SSE works over both HTTP/2 and HTTP/3, with the advantage that these protocols support multiplexing multiple streams over a single TCP connection. A client that streams updates from multiple A2A servers simultaneously benefits from HTTP/2's multiplexing, which reduces connection overhead compared to HTTP/1.1 where each SSE stream requires its own TCP connection.

CDN and caching layers. CDNs should be configured to pass SSE connections through without caching. SSE responses are not cacheable by definition (they are dynamic, real-time streams), but some CDN configurations may attempt to buffer or cache responses. Ensuring that the SSE endpoint is excluded from caching rules prevents subtle bugs where events are delayed or duplicated.

Connection limits. Browsers enforce a per-domain connection limit (typically six connections in HTTP/1.1). Server-side clients do not face this browser limitation but should still manage connection pools carefully to avoid exhausting system resources when connecting to many A2A servers simultaneously. Using HTTP/2 alleviates this concern through stream multiplexing.

Choosing Between Streaming and Polling

Not every A2A integration needs streaming. Polling with tasks/get is simpler to implement, easier to debug, and sufficient for many use cases.

Use streaming when: the client needs to display progress to a user in real time, the task produces large outputs that benefit from incremental delivery, the client needs to respond quickly to input-required states, or the application's user experience depends on showing work in progress.

Use polling when: the client submits tasks and checks results later (batch processing), the infrastructure does not reliably support long-lived HTTP connections, the task duration is predictable and short, or the client is a simple script that does not need real-time updates.

Use push notifications when: tasks run for extended periods (minutes to hours), the client cannot maintain open connections, the client runs behind a firewall that blocks incoming connections (in which case, use a webhook relay service), or the system architecture is event-driven and tasks are processed asynchronously.

Key Takeaway

A2A streaming uses Server-Sent Events over standard HTTP to deliver real-time task updates, status changes, messages, and artifacts to client agents as they are produced. Use tasks/sendSubscribe for responsive, real-time interactions, push notifications for long-running tasks, and polling with tasks/get when simplicity matters more than immediacy.