> For the complete documentation index, see [llms.txt](https://docs.synoptic.com/s/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.synoptic.com/s/websocket/subscribe-to-posts.md).

# Subscribe To Posts

Open an `on-stream-post` connection to receive stream posts as soon as they are published.

```url
wss://ws.api.synoptic.com/v1/on-stream-post?apiKey=<YOUR_READER_KEY>
```

## Quickstart

{% tabs %}
{% tab title="TypeScript" %}

```typescript
import * as ws from 'ws';

const endpoint = 'wss://ws.api.synoptic.com/v1/on-stream-post';
const apiKey = 'YOUR_READER_KEY';

const socket = new ws.WebSocket(`${endpoint}?apiKey=${apiKey}`);

socket.on('open', () => {
  console.log('Connected to Synoptic');
});

socket.on('message', (raw) => {
  const message = JSON.parse(raw.toString());

  if (message.event === 'stream.post.created') {
    console.log('New post:', message.data);
  }
});
```

{% endtab %}

{% tab title="Python" %}

```python
import json
import websocket

endpoint = 'wss://ws.api.synoptic.com/v1/on-stream-post'
api_key = 'YOUR_READER_KEY'

ws = websocket.create_connection(f'{endpoint}?apiKey={api_key}')

while True:
    message = json.loads(ws.recv())
    if message['event'] == 'stream.post.created':
        print('New post:', message['data'])
```

{% endtab %}
{% endtabs %}

## Query Parameters

* `apiKey: string` - Reader API key. You can also provide this as the `x-api-key` header.
* `streamIds: string` (optional) - Comma-separated list of stream IDs to receive.
* `lastPosts: number` (optional) - Sends up to this many recent posts in total across all connected streams. Maximum: `50`.

If `streamIds` is omitted, Synoptic sends posts from every stream the reader key can access.

## Received Events

The server sends `stream.post.created` for live posts and may also send `stream.post.last-posts` after connect when `lastPosts` is set.

```json
{
  "event": "stream.post.created",
  "data": {
    "streamId": "<STREAM_ID>",
    "id": "<POST_ID>",
    "text": "Post content",
    "createdAt": "2026-04-29T20:00:00.000Z"
  }
}
```

For custom streams, posts include `customTarget` when the post is targeted.

Source-routed posts may include `enrichments`, omitted when no enricher produced a value. Note that an empty `[]` under a key is a real answer, not a missing one. See [Enrichments](/s/integrations/enrichments.md).

## Reconnection

Build clients to reconnect automatically after disconnects, but wait a few seconds before reconnecting to avoid tight reconnect loops and rate limits.

On reconnect, pass `lastPosts` if you need a small catch-up window.
