> This page is for SDKs, version Legacy.
> For other versions, use one of these documentation indexes:
> - Current (default): https://buildwithfern.com/learn/sdks/current/llms.txt
> - Legacy: https://buildwithfern.com/learn/sdks/legacy/llms.txt

> 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.

> **You're viewing the legacy SDK docs**
>
> These pages cover SDK generation configured with `generators.yml`. New projects use `sdk-config.yml`, so see [the Current version of this page](/learn/sdks/deep-dives/sse-metadata) for up-to-date guidance. To move an existing project, follow the [SDK Config migration guide](/learn/sdks/legacy/generators/migration-guides/sdk-config), which walks through `fern sdk migrate`, and review the [`sdk-config.yml` reference](/learn/sdks/reference/sdk-config).

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.

> **Note**
>
> `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/legacy/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
});
```

> **Note**
>
> Automatic reconnection requires TypeScript SDK generator version 3.77.0+, Python 5.15.0+, Go 1.42.0+, or C# 2.71.0+.