> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cabalspy.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Trade Feed API

> GET /v1/trade-feed, the traders endpoints, notifications and the live stream: read and manage your users' Trade Feeds from your backend, and get every new trade and thesis for them.

Server-side endpoints for the [Trade Feed](https://docs.cabalspy.xyz/cabalspy-id/trade-feed.md), on the Data API with your API key. Same base URL, authentication, envelope, rate limit and errors as the rest of the [Data API](https://docs.cabalspy.xyz/cabalspy-id/data-api.md).

Every call acts in the name of one user: a person who signed in to your app with the scope `trade_feed`, or one of your [platform users](https://docs.cabalspy.xyz/cabalspy-id/white-label.md), within your plan's user limit. Every endpoint takes the user's `csid_id` or, for one of your platform users, your own `external_id`.

`trader` is always a handle (with or without @) or a csid\_id. For one of your own platform users, pass `trader_external_id` instead.

## Get the feed

`GET /v1/trade-feed`

The user's Trade Feed, newest first: every trade and thesis of the traders in it. Traders who made their trading private are left out.

### Query Parameters

**csid\_id** `string`

The user's CabalSpy ID. Alternatively pass external\_id.

**external\_id** `string`

Your own user id, for one of your platform users.

**filter** `string` (default: all)

`all`, `buys`, `sells` or `theses`.

**limit** `integer` (default: 30)

Items per page, 1 to 100.

**cursor** `string`

Cursor for the next page, the pagination.next\_cursor from the previous response.

**instance** `string`

Your Instance ID. Only needed when your account has several instances.

**api\_key** `string` *required*

Your API key. Alternatively pass it as an Authorization Bearer header.

### Usage Example

```bash cURL theme={null}
curl "https://api-id.cabalspy.xyz/v1/trade-feed?csid_id=3f9a1c2e-5b7d-4e8f-a1b2-c3d4e5f60718&limit=30" \
  -H "Authorization: Bearer YOUR_KEY"
```

```python Python theme={null}
import requests

r = requests.get("https://api-id.cabalspy.xyz/v1/trade-feed",
                 params={"csid_id": "3f9a1c2e-5b7d-4e8f-a1b2-c3d4e5f60718", "filter": "buys"},
                 headers={"Authorization": "Bearer YOUR_KEY"})
for it in r.json()["data"]:
    t = it["trade"]
    print(it["trader"]["username"], t["side"], t["symbol"], t["amount_usd"], t["label"])
```

```javascript JavaScript theme={null}
const p = new URLSearchParams({ csid_id: "3f9a1c2e-5b7d-4e8f-a1b2-c3d4e5f60718", limit: "30" });
const res = await fetch(`https://api-id.cabalspy.xyz/v1/trade-feed?${p}`, {
  headers: { Authorization: "Bearer YOUR_KEY" },
});
const { data, pagination } = await res.json();
data.forEach(it => console.log(it.type, it.trader.username, it.timestamp));
```

### Response

**data** `array`

[Trade items](#the-trade-item) and [thesis items](#the-thesis-item), newest first. Each also carries `cursor`, its position in the feed.

**pagination** `object`

limit, has\_more and next\_cursor (pass it as cursor; null on the last page).

```json Response theme={null}
{
  "success": true,
  "data": [
    {
      "id": "t3b1f0c9e8d7a6b5c4d3e2f1a0b9c8d7e",
      "type": "trade",
      "timestamp": "2026-11-01T18:40:05Z",
      "trader": {
        "csid_id": "7c2e9a14-6b3d-4f81-9e0a-5d4c3b2a1f09",
        "username": "MazeCCC",
        "name": "Maze",
        "image_url": "https://pbs.twimg.com/profile_images/1/maze_normal.jpg",
        "kol": true,
        "profile_url": "https://id.cabalspy.xyz/u/MazeCCC"
      },
      "trade": {
        "blockchain": "solana",
        "mint": "3Pkrq4MmLvDXyn1fa3sz5MekKRkZkc1iLDcz5AzWpump",
        "symbol": "CPT",
        "name": "Copycat",
        "logo": "https://...",
        "side": "sell",
        "label": "partial",
        "sold_percent": 50.0,
        "amount_native": 1.5,
        "currency": "SOL",
        "amount_usd": 225.0,
        "token_amount": 500.0,
        "price_usd": 0.45,
        "pnl_usd": 150.0,
        "pnl_percent": 200.0,
        "wallet": "7j7AA3HZR2zEjwAQEKPFh2qucLY4fqZpB9iodf39w8xW",
        "wallet_url": "https://solscan.io/account/7j7AA3HZR2zEjwAQEKPFh2qucLY4fqZpB9iodf39w8xW",
        "tx_signature": "2Bq7...",
        "tx_url": "https://solscan.io/tx/2Bq7..."
      },
      "links": {
        "token": "https://app.cabalspy.xyz/token/3Pkrq4MmLvDXyn1fa3sz5MekKRkZkc1iLDcz5AzWpump",
        "terminal": "https://app.cabalspy.xyz/token/3Pkrq4MmLvDXyn1fa3sz5MekKRkZkc1iLDcz5AzWpump",
        "profile": "https://id.cabalspy.xyz/u/MazeCCC"
      },
      "cursor": "MjAyNi0xMS0wMVQxODo0MDowNSswMDowMHx0M2Ix..."
    },
    {
      "id": "h8a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
      "type": "thesis",
      "timestamp": "2026-11-01T18:21:44Z",
      "trader": { "csid_id": "7c2e9a14-6b3d-4f81-9e0a-5d4c3b2a1f09", "username": "MazeCCC", "name": "Maze", "image_url": "https://pbs.twimg.com/profile_images/1/maze_normal.jpg", "kol": true, "profile_url": "https://id.cabalspy.xyz/u/MazeCCC" },
      "thesis": {
        "id": "8a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
        "blockchain": "solana",
        "mint": "3Pkrq4MmLvDXyn1fa3sz5MekKRkZkc1iLDcz5AzWpump",
        "symbol": "CPT",
        "name": "Copycat",
        "logo": "https://...",
        "stance": "bull",
        "body": "Dev still holding, chart is coiling.",
        "likes": 12,
        "replies": 3,
        "url": "https://id.cabalspy.xyz/try/thesis?chain=solana&mint=3Pkrq4MmLvDXyn1fa3sz5MekKRkZkc1iLDcz5AzWpump"
      },
      "links": { "token": "https://app.cabalspy.xyz/token/3Pkrq4MmLvDXyn1fa3sz5MekKRkZkc1iLDcz5AzWpump", "terminal": "https://app.cabalspy.xyz/token/3Pkrq4MmLvDXyn1fa3sz5MekKRkZkc1iLDcz5AzWpump", "profile": "https://id.cabalspy.xyz/u/MazeCCC" },
      "cursor": "MjAyNi0xMS0wMVQxODoyMTo0NCswMDowMHxoOGEx..."
    }
  ],
  "pagination": { "limit": 30, "next_cursor": null, "has_more": false },
  "meta": { "request_id": "req_5162c7cf7f87", "cached": false, "cache_age_seconds": 0, "version": "v1", "timestamp": "2026-11-01T18:41:00Z" }
}
```

### Errors

| Status | Code | Meaning |
| - | - | - |
| 400 | missing\_parameter | Neither csid\_id nor external\_id was sent. |
| 404 | [platform\_user\_not\_found](https://docs.cabalspy.xyz/cabalspy-id/errors.md#platform_user_not_found) | No platform user with this external\_id. |
| 400 | invalid\_parameter | csid\_id, filter, limit or cursor invalid. |
| 403 | [consent\_required](https://docs.cabalspy.xyz/cabalspy-id/errors.md#consent_required) | The user has not granted your app the scope `trade_feed`, or is beyond your plan's user limit. |
| 403 | [plan\_user\_limit](https://docs.cabalspy.xyz/cabalspy-id/errors.md#plan_user_limit) | The user signed in beyond your plan's user limit (an `anon_` id). |
| 429 | rate\_limit\_exceeded | Rate limit exceeded. |

## The trade item

**id** `string`

The trade's id, the same everywhere it appears (feed, webhook, stream, notifications).

**type** `string`

`trade`.

**timestamp** `string`

When the trade happened.

**trader** `object`

csid\_id, username, name, image\_url, kol and profile\_url. username and profile\_url are null for platform users.

**trade** `object`

**trade**

**blockchain, mint** `string`

The chain (solana, bnb, base, eth, rh) and token address.

**symbol, name, logo** `string`

Token info, any of them can be null.

**side** `string`

`buy` or `sell`.

**label** `string`

`new`, `add`, `partial`, `close` or `sell`. See [What appears in the feed](https://docs.cabalspy.xyz/cabalspy-id/trade-feed.md#what-appears-in-the-feed).

**sold\_percent** `number`

For `partial`: the share of the position sold, 0 to 100. Otherwise null.

**amount\_native, currency** `number`

Trade size in the chain's native coin (SOL, BNB or ETH).

**amount\_usd** `number`

Trade size in USD, or null when unknown.

**token\_amount** `number`

Tokens bought or sold.

**price\_usd** `number`

Price per token in USD, or null when unknown.

**pnl\_usd, pnl\_percent** `number`

Sells with a known entry: realized PnL of this sell against the average entry of the position. Otherwise null.

**wallet, wallet\_url** `string`

The trader's verified wallet and its explorer page.

**tx\_signature, tx\_url** `string`

The transaction and its explorer page.

**links** `object`

token (the CabalSpy Terminal on Solana and BNB Chain, otherwise the token's theses on CabalSpy ID), terminal (null outside Solana and BNB Chain) and profile.

## The thesis item

**id** `string`

`h` followed by the thesis id.

**type** `string`

`thesis`.

**timestamp, trader, links** `object`

As for a trade.

**thesis** `object`

id, blockchain, mint, symbol, name, logo, stance (`bull`, `bear` or null), body, likes, replies and url. See [Token theses](https://docs.cabalspy.xyz/cabalspy-id/theses.md).

## List traders

`GET /v1/trade-feed/traders`

The traders in the user's Trade Feed, most recently added first, with the user's alert and [copy](https://docs.cabalspy.xyz/cabalspy-id/copy-trading-settings.md) settings for each.

### Query Parameters

**csid\_id** `string`

The user's CabalSpy ID. Alternatively pass external\_id.

**external\_id** `string`

Your own user id, for one of your platform users.

**limit** `integer` (default: 100)

Traders per page, 1 to 300.

**cursor** `string`

Cursor for the next page.

**api\_key** `string` *required*

Your API key. Alternatively pass it as an Authorization Bearer header.

```bash cURL theme={null}
curl "https://api-id.cabalspy.xyz/v1/trade-feed/traders?csid_id=3f9a1c2e-5b7d-4e8f-a1b2-c3d4e5f60718" \
  -H "Authorization: Bearer YOUR_KEY"
```

```python Python theme={null}
import requests

r = requests.get("https://api-id.cabalspy.xyz/v1/trade-feed/traders",
                 params={"csid_id": "3f9a1c2e-5b7d-4e8f-a1b2-c3d4e5f60718"},
                 headers={"Authorization": "Bearer YOUR_KEY"}).json()
print(r["pagination"]["total"], "of", r["pagination"]["max"])
for t in r["data"]:
    print(t["trader"]["username"], t["settings"]["muted"], t["settings"]["copy"]["enabled"])
```

```javascript JavaScript theme={null}
const res = await fetch("https://api-id.cabalspy.xyz/v1/trade-feed/traders?csid_id=3f9a1c2e-5b7d-4e8f-a1b2-c3d4e5f60718", {
  headers: { Authorization: "Bearer YOUR_KEY" },
});
const { data } = await res.json();
data.forEach(t => console.log(t.trader.username, t.in_trade_feeds, t.trading_public));
```

### Response

**data** `array`

**entry**

**trader** `object`

csid\_id, username, name, image\_url, kol and profile\_url.

**trading\_public** `boolean`

`false` while the trader keeps their trading private. They stay in the list but nothing of them shows in the feed.

**added\_at** `string`

When the user added the trader.

**in\_trade\_feeds** `integer`

In how many Trade Feeds the trader is.

**settings** `object`

notify\_buys, notify\_sells, notify\_theses, min\_native (`{"SOL", "BNB", "ETH"}`), muted, and copy (the [copy settings](https://docs.cabalspy.xyz/cabalspy-id/copy-trading-settings.md)).

**pagination** `object`

limit, total (traders in the Trade Feed), max (300), has\_more and next\_cursor.

```json Response theme={null}
{
  "success": true,
  "data": [
    {
      "trader": { "csid_id": "7c2e9a14-6b3d-4f81-9e0a-5d4c3b2a1f09", "username": "MazeCCC", "name": "Maze", "image_url": "https://pbs.twimg.com/profile_images/1/maze_normal.jpg", "kol": true, "profile_url": "https://id.cabalspy.xyz/u/MazeCCC" },
      "trading_public": true,
      "added_at": "2026-10-20T09:12:00+00:00",
      "in_trade_feeds": 128,
      "settings": {
        "notify_buys": true,
        "notify_sells": true,
        "notify_theses": true,
        "min_native": { "SOL": 0.5, "BNB": 0.0, "ETH": 0.0 },
        "muted": false,
        "copy": { "enabled": true, "mode": "fixed_usd", "amount_usd": 25.0, "percent": 100.0, "max_usd": 20.0, "sides": "both", "slippage_bps": 300, "updated_at": "2026-11-01T17:58:40Z" }
      }
    }
  ],
  "pagination": { "limit": 100, "total": 1, "max": 300, "next_cursor": null, "has_more": false },
  "meta": { "request_id": "req_5162c7cf7f87", "cached": false, "cache_age_seconds": 0, "version": "v1", "timestamp": "2026-11-01T18:00:00Z" }
}
```

## Add a trader

`POST /v1/trade-feed/traders`

Adds a trader to the user's Trade Feed, with the user's default alert settings unless you pass others. Answers **201** when the trader was added, **200** when they were already there; then the settings you pass are updated.

### Body

**csid\_id** `string`

The user's CabalSpy ID. Alternatively pass external\_id.

**external\_id** `string`

The user, by your own user id.

**trader** `string` *required*

Handle (with or without @) or csid\_id. Alternatively pass trader\_external\_id.

**trader\_external\_id** `string`

One of your own platform users, by your user id.

**notify\_buys, notify\_sells, notify\_theses** `boolean`

Alerts for buys, sells and theses of this trader.

**min\_sol, min\_bnb, min\_eth** `number`

Minimum trade size in the native coin for alerts, 0 to 1,000,000. 0 means every trade.

**muted** `boolean`

No alerts for this trader.

```bash cURL theme={null}
curl -X POST "https://api-id.cabalspy.xyz/v1/trade-feed/traders" \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_id": "user_8412", "trader": "@MazeCCC", "min_sol": 0.5}'
```

```python Python theme={null}
import requests

r = requests.post("https://api-id.cabalspy.xyz/v1/trade-feed/traders",
                  json={"csid_id": "3f9a1c2e-5b7d-4e8f-a1b2-c3d4e5f60718", "trader": "MazeCCC", "notify_sells": False},
                  headers={"Authorization": "Bearer YOUR_KEY"})
print(r.status_code, r.json()["data"]["created"])
```

```javascript JavaScript theme={null}
const res = await fetch("https://api-id.cabalspy.xyz/v1/trade-feed/traders", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ external_id: "user_8412", trader_external_id: "user_77" }),
});
console.log(res.status, (await res.json()).data.settings);
```

```json Response theme={null}
{
  "success": true,
  "data": {
    "in_trade_feed": true,
    "created": true,
    "trader": { "csid_id": "7c2e9a14-6b3d-4f81-9e0a-5d4c3b2a1f09", "username": "MazeCCC", "name": "Maze", "image_url": "https://pbs.twimg.com/profile_images/1/maze_normal.jpg", "kol": true, "profile_url": "https://id.cabalspy.xyz/u/MazeCCC" },
    "settings": {
      "notify_buys": true,
      "notify_sells": true,
      "notify_theses": true,
      "min_native": { "SOL": 0.5, "BNB": 0.0, "ETH": 0.0 },
      "muted": false,
      "copy": { "enabled": false, "mode": "fixed_usd", "amount_usd": 50.0, "percent": 100.0, "max_usd": null, "sides": "both", "slippage_bps": 300, "updated_at": null }
    }
  },
  "meta": { "request_id": "req_5162c7cf7f87", "cached": false, "cache_age_seconds": 0, "version": "v1", "timestamp": "2026-11-01T18:00:00Z" }
}
```

### Errors

| Status | Code | Meaning |
| - | - | - |
| 400 | missing\_parameter | No user or no trader was sent. |
| 400 | invalid\_parameter | A minimum trade size is not a number from 0 to 1,000,000. |
| 400 | [cannot\_add\_self](https://docs.cabalspy.xyz/cabalspy-id/errors.md#cannot_add_self) | The user tried to add themselves. |
| 403 | [consent\_required](https://docs.cabalspy.xyz/cabalspy-id/errors.md#consent_required) | The user has not granted your app the scope `trade_feed`, or is beyond your plan's user limit. |
| 403 | [trading\_private](https://docs.cabalspy.xyz/cabalspy-id/errors.md#trading_private) | The trader keeps their trading private. |
| 404 | [platform\_user\_not\_found](https://docs.cabalspy.xyz/cabalspy-id/errors.md#platform_user_not_found) | No platform user with this external\_id or trader\_external\_id. |
| 404 | [trader\_not\_found](https://docs.cabalspy.xyz/cabalspy-id/errors.md#trader_not_found) | No CabalSpy ID trader with this handle or csid\_id. |
| 409 | [trade\_feed\_full](https://docs.cabalspy.xyz/cabalspy-id/errors.md#trade_feed_full) | The Trade Feed already has 300 traders. |
| 429 | [rate\_limited](https://docs.cabalspy.xyz/cabalspy-id/errors.md#rate_limited) | More than 120 Trade Feed changes for this user in an hour. |

## Remove a trader

`POST /v1/trade-feed/traders/delete`

Removes a trader from the user's Trade Feed. This also ends [copying](https://docs.cabalspy.xyz/cabalspy-id/copy-trading.md) of that trader. `removed` is `false` when the trader was not in the Trade Feed.

### Body

**csid\_id** `string`

The user's CabalSpy ID. Alternatively pass external\_id.

**external\_id** `string`

The user, by your own user id.

**trader** `string` *required*

Handle or csid\_id. Alternatively pass trader\_external\_id.

```bash cURL theme={null}
curl -X POST "https://api-id.cabalspy.xyz/v1/trade-feed/traders/delete" \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_id": "user_8412", "trader": "MazeCCC"}'
```

```python Python theme={null}
import requests

r = requests.post("https://api-id.cabalspy.xyz/v1/trade-feed/traders/delete",
                  json={"csid_id": "3f9a1c2e-5b7d-4e8f-a1b2-c3d4e5f60718", "trader": "MazeCCC"},
                  headers={"Authorization": "Bearer YOUR_KEY"})
print(r.json()["data"]["removed"])
```

```javascript JavaScript theme={null}
const res = await fetch("https://api-id.cabalspy.xyz/v1/trade-feed/traders/delete", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ external_id: "user_8412", trader: "MazeCCC" }),
});
console.log((await res.json()).data);
```

```json Response theme={null}
{
  "success": true,
  "data": { "in_trade_feed": false, "removed": true },
  "meta": { "request_id": "req_5162c7cf7f87", "cached": false, "cache_age_seconds": 0, "version": "v1", "timestamp": "2026-11-01T18:00:00Z" }
}
```

## Notifications

`GET /v1/trade-feed/notifications`

The user's recent Trade Feed alerts, newest first: every trade and thesis that passed the user's [filters](https://docs.cabalspy.xyz/cabalspy-id/trade-feed.md#alerts-and-filters) for that trader. Useful to show an alert inbox when you do not use webhooks.

### Query Parameters

**csid\_id** `string`

The user's CabalSpy ID. Alternatively pass external\_id.

**external\_id** `string`

Your own user id, for one of your platform users.

**limit** `integer` (default: 50)

Alerts per page, 1 to 100.

**cursor** `string`

Cursor for the next page.

**api\_key** `string` *required*

Your API key. Alternatively pass it as an Authorization Bearer header.

```bash cURL theme={null}
curl "https://api-id.cabalspy.xyz/v1/trade-feed/notifications?csid_id=3f9a1c2e-5b7d-4e8f-a1b2-c3d4e5f60718&limit=20" \
  -H "Authorization: Bearer YOUR_KEY"
```

```python Python theme={null}
import requests

r = requests.get("https://api-id.cabalspy.xyz/v1/trade-feed/notifications",
                 params={"csid_id": "3f9a1c2e-5b7d-4e8f-a1b2-c3d4e5f60718"},
                 headers={"Authorization": "Bearer YOUR_KEY"})
for n in r.json()["data"]:
    print(n["created_at"], n["text"])
```

```javascript JavaScript theme={null}
const res = await fetch("https://api-id.cabalspy.xyz/v1/trade-feed/notifications?csid_id=3f9a1c2e-5b7d-4e8f-a1b2-c3d4e5f60718", {
  headers: { Authorization: "Bearer YOUR_KEY" },
});
(await res.json()).data.forEach(n => console.log(n.event, n.text));
```

**data** `array`

id (a number), event (`trade_feed.trade` or `trade_feed.thesis`), text (a ready-made line in plain text), created\_at and data (the [trade item](#the-trade-item) or [thesis item](#the-thesis-item)).

```json Response theme={null}
{
  "success": true,
  "data": [
    {
      "id": 48213,
      "event": "trade_feed.trade",
      "text": "Trade Feed: @MazeCCC sold $CPT — 1.5 SOL ($225) · sold 50% · PnL +$150 (+200%)",
      "created_at": "2026-11-01T18:40:06Z",
      "data": { "id": "t3b1f0c9e8d7a6b5c4d3e2f1a0b9c8d7e", "type": "trade", "...": "the trade item" }
    }
  ],
  "pagination": { "limit": 50, "next_cursor": null, "has_more": false },
  "meta": { "request_id": "req_5162c7cf7f87", "cached": false, "cache_age_seconds": 0, "version": "v1", "timestamp": "2026-11-01T18:41:00Z" }
}
```

## Live stream

`wss://api-id.cabalspy.xyz/v1/trade-feed/stream?api_key=YOUR_KEY`

Every new trade and thesis that concerns your users, live, on every plan. Each event says which of your users to tell: `recipients` lists the `csid_id` of your users who have the trader in their Trade Feed and whose filters let the event through, and `recipients_external` your own ids of those who are platform users. Events nobody of yours should get are not sent.

Pass the key as `api_key`, `apiKey` or an Authorization Bearer header, and `instance` when your account has several instances.

| Event | When | Fields |
| - | - | - |
| `ready` | Right after connecting. | `data.events`: `["trade_feed.trade", "trade_feed.thesis"]`. |
| `trade_feed.trade` | A trader in your users' Trade Feeds traded. | `id` (event id), `data`: the [trade item](#the-trade-item) plus `recipients` and `recipients_external`. |
| `trade_feed.thesis` | A trader posted a thesis. | `id`, `data`: the [thesis item](#the-thesis-item) plus `recipients` and `recipients_external`. |
| `heartbeat` | After 30 seconds without a message from you. | `ts`: Unix time in seconds. |
| `pong` | After you send `ping`. | None. |
| `error` | The connection is refused. | `error.code` and `error.message`, then the server closes with code 4403. |

```javascript JavaScript theme={null}
const ws = new WebSocket("wss://api-id.cabalspy.xyz/v1/trade-feed/stream?api_key=YOUR_KEY");
ws.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  if (msg.event === "trade_feed.trade") {
    const { trader, trade, recipients_external } = msg.data;
    recipients_external.forEach(uid => notify(uid, `${trader.username} ${trade.side === "buy" ? "bought" : "sold"} ${trade.symbol}`));
  }
};
```

```python Python theme={null}
import asyncio, json, websockets

async def main():
    async with websockets.connect("wss://api-id.cabalspy.xyz/v1/trade-feed/stream?api_key=YOUR_KEY") as ws:
        async for raw in ws:
            msg = json.loads(raw)
            if msg["event"] in ("trade_feed.trade", "trade_feed.thesis"):
                print(msg["event"], msg["data"]["trader"]["username"], msg["data"]["recipients"])

asyncio.run(main())
```

```json trade_feed.trade theme={null}
{
  "event": "trade_feed.trade",
  "id": "0f6c2a9e-3d1b-5c7e-9a2f-4b8d6e1c3a5f",
  "data": {
    "id": "t3b1f0c9e8d7a6b5c4d3e2f1a0b9c8d7e",
    "type": "trade",
    "timestamp": "2026-11-01T18:40:05Z",
    "trader": { "csid_id": "7c2e9a14-6b3d-4f81-9e0a-5d4c3b2a1f09", "username": "MazeCCC", "...": "..." },
    "trade": { "side": "sell", "label": "partial", "sold_percent": 50.0, "...": "the trade" },
    "links": { "...": "..." },
    "recipients": ["3f9a1c2e-5b7d-4e8f-a1b2-c3d4e5f60718"],
    "recipients_external": ["user_8412"]
  }
}
```

Send `ping` at any time for a `pong`. After 30 seconds of silence the server sends a `heartbeat` and checks that your key is still valid. Each API key can hold at most 5 Trade Feed stream connections, counted separately from the other streams; more get `too_many_connections`. Events made while you were disconnected are not replayed; read [Notifications](#notifications) or the feed to catch up.

The same events are available as [webhooks](https://docs.cabalspy.xyz/cabalspy-id/webhooks.md#trade-feed-events).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.