# A live stream of your own fills, orders, trades and accounts

`GET https://app.skylit.ai/api/nexus/v1/trading/stream`

API: Nexus.

Open one stream and Nexus pushes your own changes the moment they happen, so your bot stops asking "did it fill yet?". It's Server-Sent Events (`text/event-stream`): one long response with an event per change. Run it from your bot: a browser's `EventSource` can't send your key in a header, and your key shouldn't be in a browser anyway.

- **Same key, same checks** as every route. Opening it counts as one read. Events and heartbeats don't.
- **Only yours.** Nobody else's trades ever show up. Your agent account's events come only to you. Test orders never show up: they aren't trades. Futures events only come if your account has futures.
- **No market data.** Quotes aren't in it.

| Event | When | `data` |
|---|---|---|
| `connected` | Once, first (no `id`) | `account`, `resumed`, `futures`, `heartbeatSeconds` |
| `resync` | First thing on a new connection, and any time the server lost track | `{reason}`. Re-read your state with the REST routes |
| `trade` | An options or stock trade was opened, added to, trimmed, closed or expired | `change`, `book`, `at`, `fill` (`quantity`, `price`; `id` only when known, left out today), `trade` (as `GET /api/nexus/v1/trades/{id}` returns it) |
| `order` | A resting order changed | `change`, `assetClass`, `book`, `order` (as the order read returns it) |
| `wallet` | Your options and stocks cash or buying power moved | `book`, `wallet` (as the wallet read returns it) |
| `account` | A futures account changed | `book`, `account` (as the account read returns it) |
| `heartbeat` | Every 15 seconds (no `id`) | `{at}` |
| `closed` | We ended the stream. Fix the reason before you reconnect | `{reason}`: `invalid_api_key`, `account_suspended`, `nexus_access_denied` or `trades_api_disabled` |
| `reconnect` | The server is restarting | `{reason: "server_restart"}`. Reconnect right away |

An order's `change` is `working`, `held` (a futures stop or target waiting for its entry), `modified`, `triggered` (a stop went off), `partial_fill`, `filled`, `cancelled`, `rejected` or `expired`. A trade's `change` is `opened`, `added`, `trimmed`, `closed` or `expired`. Orders you placed in Nexus show up too.

**Dropped connections.** Every data event has an `id`. Reconnect with the last one as `Last-Event-ID`. If the server you land on still has what you missed (your last 128 events, for 2 minutes after you drop), you get them and `connected` says `"resumed": true`. If not, the first event after `connected` is `resync`. So on `resync`, read your open trades, working orders, wallet and futures accounts again, then keep going.

**Limits.** 3 streams per key and 6 per account at once, past that `429 stream_limit_reached` with `Retry-After`. A heartbeat comes every 15 seconds: hear nothing for 45 and the connection's dead, so reconnect. A revoked key's stream ends within about 2 minutes. `capabilities` has a `stream` block with these numbers.

## Authentication

Send your Skylit API key as a bearer token: `Authorization: Bearer <key>`. No other header is accepted.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `account` | query | string | no | `main` for your own trades and accounts, `agent` for your bot's. Leave it out (or send `all`) for both. Every event says which `book` it's on. (one of `main`, `agent`, `all`) |
| `Last-Event-ID` | header | string | no | The `id` of the last event you got, to pick up where you left off after a drop. |
| `lastEventId` | query | string | no | The same as `Last-Event-ID`, for clients that can't set the header. The header wins if both are sent. |

## Example request

```bash
curl -N "https://app.skylit.ai/api/nexus/v1/trading/stream" \
  -H "Authorization: Bearer $SKYLIT_API_KEY" \
  -H "Accept: text/event-stream"
```

## Responses

### 200

The stream. It stays open until you close it or the server sends `closed` or `reconnect`.

The first frames of a stream and a fill, shown as JSON. On the wire each one is id, event and data lines. The order and trade objects are cut short here.:

```json
[
  {
    "event": "connected",
    "data": {
      "account": "all",
      "resumed": false,
      "futures": true,
      "heartbeatSeconds": 15
    }
  },
  {
    "id": "5c1f0e9a2b7d-1041",
    "event": "resync",
    "data": {
      "reason": "connected"
    }
  },
  {
    "id": "5c1f0e9a2b7d-1042",
    "event": "order",
    "data": {
      "change": "filled",
      "assetClass": "options",
      "book": "main",
      "order": {
        "id": "8f0c2d1e-6b3a-4c5d-9e7f-1a2b3c4d5e6f",
        "status": "filled",
        "fillPrice": 1.98,
        "filledQuantity": 2,
        "tradeId": "41aa7c3e-2d1b-4f6a-8e9c-0b1d2e3f4a5b"
      }
    }
  },
  {
    "id": "5c1f0e9a2b7d-1043",
    "event": "trade",
    "data": {
      "change": "opened",
      "book": "main",
      "at": "2026-10-12T14:31:07Z",
      "fill": {
        "quantity": 2,
        "price": 1.98
      },
      "trade": {
        "id": "41aa7c3e-2d1b-4f6a-8e9c-0b1d2e3f4a5b",
        "ticker": "SPY"
      }
    }
  },
  {
    "event": "heartbeat",
    "data": {
      "at": "2026-10-12T14:31:15Z"
    }
  }
]
```

### 400

The request was refused before anything was traded. Codes: `bad_request`, `bad_contract`, `invalid_quantity`, `invalid_price`, `unsupported_asset_class`, `unsupported_order_type`, `only_market_orders`, `limit_price_required` (a limit with no `limitPrice`), `invalid_limit_price` (not in cents, or outside 0.01 to 99,999.99 for options or 999,999.99 for stocks), `stop_price_required` (a stop with no `stopPrice`), `invalid_stop_price` (not in cents, or outside 0.01 to 999,999.99), `invalid_take_profit`, `test_orders_unavailable`, `test_modify_unavailable`.

A typo in the body.:

```json
{
  "error": {
    "code": "bad_request",
    "message": "Unknown field \"quantitty\". See the trades API docs for the fields you can send."
  }
}
```

### 401

No key, or a key that isn't valid. Codes: `missing_api_key`, `invalid_api_key`.

No key sent.:

```json
{
  "error": {
    "code": "missing_api_key",
    "message": "Send your Skylit API key in the Authorization header: Authorization: Bearer YOUR_SKYLIT_API_KEY."
  }
}
```

### 403

The key works but the account can't do this. Codes: `account_suspended`, `nexus_access_denied`, `futures_access_denied`.

The account doesn't include Nexus.:

```json
{
  "error": {
    "code": "nexus_access_denied",
    "message": "This Skylit account doesn't include Nexus, so it can't place Nexus trades."
  }
}
```

### 429

Over a rate limit. Wait `Retry-After` seconds, then retry. Codes: `rate_limited`, and `stream_limit_reached` (too many live streams open: 3 per key, 6 per account).

Headers: `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`.

Too many writes from this key.:

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests (limit 30). Slow down and retry in 12 seconds."
  }
}
```

### 500

Something went wrong on our side. If you sent a `clientOrderId`, retry with the same one. Code: `internal_error`.

Retry with the same clientOrderId.:

```json
{
  "error": {
    "code": "internal_error",
    "message": "Something went wrong on our side. If you sent a clientOrderId, retry with the same one (it can't fill twice). Otherwise check your account before retrying."
  }
}
```

### 503

Try again in a moment (honour `Retry-After` when it's sent). Codes: `stale_quote` (the quote, or a stock's last trade, is more than 15 seconds old, so nothing filled), `no_live_price`, `rules_pending`, `unavailable`, `auth_unavailable`, `access_check_unavailable`, `api_keys_unavailable`, `trades_api_disabled`, `stream_unavailable` (the live stream isn't on, or the server is restarting: try again shortly, or poll the reads).

Headers: `Retry-After`.

No live price right now.:

```json
{
  "error": {
    "code": "no_live_price",
    "message": "There's no live price for this contract right now, so nothing was filled. Try again in a moment."
  }
}
```
