> For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://buildwithfern.com/learn/llms.txt. # SSE metadata access > Access server-sent event metadata (event ID, event type, retry interval) in Fern-generated SDKs for stream resumption and protocol-level control. When your API uses [server-sent events](/learn/api-definitions/openapi/endpoints/sse#server-sent-events), iterating the generated SDK's streaming response yields parsed data objects. To also read the SSE protocol fields — event ID, event type, and retry interval — TypeScript, Python, and Go SDKs expose metadata-aware iteration, typically used to [resume a stream](#stream-resumption) by event ID. ## Metadata-aware iteration Each event exposes the parsed data alongside its protocol fields. Default iteration is unchanged, so opting in is fully backward compatible. #### TypeScript ```ts const stream = await client.plants.stream({ query: "fern" }); for await (const event of stream.withMetadata()) { event.data // parsed response object (same type as default iteration) event.id // SSE event ID (string | undefined) event.event // SSE event type (string | undefined) event.retry // SSE retry interval in ms (number | undefined) } ``` #### Python ```python stream = client.plants.stream(query="fern") for event in stream.with_metadata(): event.data # parsed response object (same type as default iteration) event.id # SSE event ID (str | None) event.event # SSE event type (str | None) event.retry # SSE retry interval in ms (int | None) ``` #### Go ```go stream := client.Plants.Stream(ctx, &PlantRequest{Query: "fern"}) defer stream.Close() for { event, err := stream.RecvEvent() if err != nil { break } event.Data // parsed response object (same type as Recv) event.ID // SSE event ID (string) event.Event // SSE event type (string) event.Retry // SSE retry interval in ms (int) } // Read the most recent event ID without consuming the next event: lastID := stream.LastEventID() ``` Each stream owns the underlying HTTP response, and releases it differently per language: * **TypeScript**: the body is released when iteration ends and cancelled when you break out of the loop. An `abortSignal` in the request options stops the stream from outside. * **Python**: the stream is lazy, issuing the request on first iteration, and releases the response when it's exhausted, when iteration raises, on `close()`, or on exiting a `with` block. `AsyncStream` supports `async with` and is awaitable. * **Go**: the stream never closes the body on its own, so `defer stream.Close()` is required. `withMetadata()` requires TypeScript SDK generator version 3.73.0+, and `RecvEvent()` requires Go SDK generator version 1.32.0+. In Python, `with_metadata()` requires generator version 5.29.0+ with [`stream_abstraction`](/learn/sdks/generators/python/configuration#stream_abstraction) enabled. ## Stream resumption The event ID is useful for resuming a stream via the standard `Last-Event-ID` header: store the last received ID as you iterate, then pass it back to the server on reconnection. This requires server-side support for the `Last-Event-ID` header. ## Automatic reconnection Mark an SSE endpoint [`resumable`](/learn/api-definitions/openapi/endpoints/sse#resumable-streams) in your API definition (`x-fern-streaming.resumable: true` in OpenAPI, or `response-stream.resumable: true` in a Fern Definition) to have the SDK handle resumption for you. On a mid-stream drop, the SDK reconnects transparently, resending the last dispatched event ID in the `Last-Event-ID` header so iteration continues without gaps. No manual event-ID tracking is required. Reconnection honors the server's `retry:` directive for the reconnect delay, falling back to a 1-second default and capped at 30 seconds. The SDK retries up to 5 consecutive times by default; the counter resets whenever an event is received. Configure a [terminator](/learn/api-definitions/openapi/endpoints/sse#terminator-message) on the endpoint so the SDK can tell a completed stream from a dropped connection. Both the attempt cap and an on/off toggle are configurable per client and per request: #### TypeScript ```ts {2} const stream = await client.plants.stream({ query: "fern" }, { stream: { reconnectionEnabled: true, maxReconnectionAttempts: 3 } }); ``` #### Python ```python {3-4} stream = client.plants.stream( query="fern", stream_reconnection_enabled=True, max_stream_reconnection_attempts=3, ) ``` #### Go ```go {4} stream := client.Plants.Stream( ctx, &PlantRequest{Query: "fern"}, option.WithMaxStreamReconnectAttempts(3), // or option.WithoutStreamReconnection() ) ``` #### C\# ```csharp {2} var stream = await client.Plants.StreamAsync(new PlantRequest { Query = "fern" }, new RequestOptions { MaxStreamReconnectAttempts = 3 // set DisableStreamReconnection = true to turn off }); ``` Automatic reconnection requires TypeScript SDK generator version 3.77.0+, Python 5.15.0+, Go 1.42.0+, or C# 2.71.0+. > Access server-sent event metadata (event ID, event type, retry interval) in Fern-generated SDKs for stream resumption and protocol-level control.