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

# Real-Time Data API

> REST and WebSocket market-data feed at data.simplefunctions.dev/v1 — tickers, search, snapshots, movers, orderbooks, trades, candles, featured.

Use the Real-Time Data API when you need raw market data for a terminal, dashboard, bot, or trading agent.

This surface is separate from `simplefunctions.dev/api/public/*`. The public API is for analytical objects and agent workflows. The data API is for fast market data.

Last verified: 2026-05-06.

## Base URLs

REST:

```text theme={null}
https://data.simplefunctions.dev/v1
```

Current public WebSocket:

```text theme={null}
wss://app.simplefunctions.dev/ws
```

Do not use `wss://data.simplefunctions.dev/v1/ws` yet. It is the intended canonical data-domain WebSocket name, but current routing can return `426 Upgrade Required`. Use `wss://app.simplefunctions.dev/ws` until the WS route is moved off Vercel or a direct WS CNAME is configured.

## Data conventions

| Field          | Convention                                              |
| -------------- | ------------------------------------------------------- |
| Prices         | Probabilities in `[0, 1]`, not cents.                   |
| `generated_at` | Unix seconds.                                           |
| `closeTime`    | Unix seconds.                                           |
| `ts`           | Unix milliseconds.                                      |
| Compression    | Large REST responses are gzip-compressed by the origin. |

## Coverage and hydration

The data API is backed by an in-memory market registry plus venue REST fallbacks.

| Path                                                                                                                                | Coverage behavior                                                                                                       |
| ----------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Browse endpoints such as `/v1/markets`, `/v1/snapshot`, `/v1/movers`, and broad `/v1/search`                                        | Use the warm registry and recent in-memory samples. They are fast and intentionally bounded.                            |
| Exact Kalshi ticker reads such as `/v1/markets/{ticker}`, `/v1/orderbook/{ticker}`, `/v1/candles/{ticker}`, and exact ticker search | Attempt direct Kalshi hydration when the ticker missed the warm registry.                                               |
| Trades                                                                                                                              | Return recent in-memory trade prints. Empty trades means no recent cached prints, not necessarily no historical volume. |

Hydration is exact-ticker only. It is not a general text-search crawler.

## REST endpoints

| Endpoint                                   | Use                                          | Cache                        |
| ------------------------------------------ | -------------------------------------------- | ---------------------------- |
| `GET /v1/heartbeat`                        | Health/status payload.                       | `max-age=10`                 |
| `GET /v1/markets?q=&venue=`                | Top tracked markets or registry search.      | `max-age=3`                  |
| `GET /v1/markets/featured?n=50`            | Heat-ranked featured markets.                | `max-age=3`                  |
| `GET /v1/markets/{ticker}`                 | One market snapshot.                         | `max-age=2`                  |
| `GET /v1/search?q=&limit=&venue=&strict=`  | Autocomplete-grade ticker/title search.      | `max-age=5`                  |
| `GET /v1/snapshot`                         | Compact full active-universe price snapshot. | `max-age=2`                  |
| `GET /v1/movers?window=&n=&minVol=&dir=`   | Top price movers over a recent window.       | `max-age=5`                  |
| `GET /v1/orderbook/{ticker}`               | Top-of-book/depth snapshot.                  | `max-age=1`                  |
| `GET /v1/candles/{ticker}?tf=1h&limit=500` | OHLCV candles.                               | `max-age=5` or `15` for `1d` |
| `GET /v1/trades/{ticker}?limit=50`         | Recent trade prints.                         | `max-age=1`                  |

## Heartbeat

```bash theme={null}
curl "https://data.simplefunctions.dev/v1/heartbeat"
```

```json theme={null}
{
  "markets_tracked": 9333,
  "ws_clients": 3,
  "top_volume_market": "KXPRESNOMD-28-GN",
  "uptime_s": 12345,
  "generated_at": 1777553300
}
```

## Markets

```bash theme={null}
curl "https://data.simplefunctions.dev/v1/markets"
curl "https://data.simplefunctions.dev/v1/markets?q=newsom&venue=kalshi"
curl "https://data.simplefunctions.dev/v1/markets?q=KXHORMUZWEEKLY-26MAY10-T40"
curl "https://data.simplefunctions.dev/v1/markets/featured?n=50"
curl "https://data.simplefunctions.dev/v1/markets/KXPRESNOMD-28-GN"
```

Market object:

```json theme={null}
{
  "ticker": "KXPRESNOMD-28-GN",
  "venue": "kalshi",
  "title": "Will Gavin Newsom be the Democratic Presidential nominee in 2028?",
  "lastPrice": 0.26,
  "volume24h": 12345,
  "closeTime": 1853884800,
  "bestBid": 0.25,
  "bestAsk": 0.27,
  "heat": 81.4
}
```

## Search

```bash theme={null}
curl "https://data.simplefunctions.dev/v1/search?q=newsom&limit=10"
curl "https://data.simplefunctions.dev/v1/search?q=rate%20cut&venue=kalshi&strict=0"
curl "https://data.simplefunctions.dev/v1/search?q=KXHORMUZWEEKLY-26MAY10-T40&strict=0"
```

Parameters:

| Parameter | Values                 | Use                                                            |
| --------- | ---------------------- | -------------------------------------------------------------- |
| `q`       | string                 | Required query.                                                |
| `limit`   | `1` to `50`            | Result cap. Default `10`.                                      |
| `venue`   | `kalshi`, `polymarket` | Optional venue filter.                                         |
| `strict`  | `1`, `0`               | `strict=0` enables looser substring matching for short tokens. |

Response:

```json theme={null}
{
  "query": "newsom",
  "results": [
    {
      "ticker": "KXPRESNOMD-28-GN",
      "venue": "kalshi",
      "title": "Will Gavin Newsom be the Democratic Presidential nominee in 2028?",
      "lastPrice": 0.26,
      "volume24h": 12345,
      "score": 400
    }
  ]
}
```

## Raw snapshot

```bash theme={null}
curl "https://data.simplefunctions.dev/v1/snapshot"
```

Use this for bot cold-start. It intentionally omits title, close time, and heat to keep the payload small. Markets pinned outside `[0.01, 0.99]` are excluded.

```json theme={null}
{
  "generated_at": 1777553300,
  "count": 9000,
  "markets": [
    {
      "ticker": "KXPRESNOMD-28-GN",
      "venue": "kalshi",
      "last": 0.26,
      "bid": 0.25,
      "ask": 0.27,
      "vol24h": 12345
    }
  ]
}
```

## Movers

```bash theme={null}
curl "https://data.simplefunctions.dev/v1/movers?window=1h&n=50&minVol=1000&dir=both"
```

Parameters:

| Parameter | Values                                     | Use                               |
| --------- | ------------------------------------------ | --------------------------------- |
| `window`  | `1m`, `5m`, `15m`, `1h`, `4h`, `24h`, `1d` | Move window. Default `1h`.        |
| `n`       | `10` to `200`                              | Result cap. Default `50`.         |
| `minVol`  | number                                     | 24h volume floor. Default `1000`. |
| `dir`     | `up`, `down`, `both`                       | Direction filter. Default `both`. |

`/v1/movers` is powered by in-memory tick/trade samples. After deploy or reconnect, short windows can return `count: 0` until samples accumulate.

## Orderbook, candles, trades

```bash theme={null}
curl "https://data.simplefunctions.dev/v1/orderbook/KXPRESNOMD-28-GN"
curl "https://data.simplefunctions.dev/v1/candles/KXPRESNOMD-28-GN?tf=1h&limit=500"
curl "https://data.simplefunctions.dev/v1/trades/KXPRESNOMD-28-GN?limit=50"
```

For valid Kalshi tickers that are not already in the warm registry, the market, orderbook, and candle paths attempt direct venue hydration before returning the current cache view.

Orderbook:

```json theme={null}
{
  "ticker": "KXPRESNOMD-28-GN",
  "bids": [[0.25, 1200]],
  "asks": [[0.27, 700]],
  "ts": 1777553300123
}
```

Candles:

```json theme={null}
{
  "ticker": "KXPRESNOMD-28-GN",
  "timeframe": "1h",
  "candles": [
    { "t": 1777550400000, "o": 0.25, "h": 0.27, "l": 0.24, "c": 0.26, "v": 900 }
  ]
}
```

Trades:

```json theme={null}
{
  "ticker": "KXPRESNOMD-28-GN",
  "trades": [
    {
      "ticker": "KXPRESNOMD-28-GN",
      "venue": "kalshi",
      "price": 0.26,
      "size": 10,
      "side": "buy",
      "ts": 1777553300123
    }
  ]
}
```

## WebSocket

```js theme={null}
const ws = new WebSocket('wss://app.simplefunctions.dev/ws')

ws.addEventListener('open', () => {
  ws.send(JSON.stringify({
    action: 'subscribe',
    topics: [
      'featured',
      'ticker:KXPRESNOMD-28-GN',
      'orderbook:KXPRESNOMD-28-GN',
      'trade:KXPRESNOMD-28-GN',
      'candle:KXPRESNOMD-28-GN:1m'
    ],
  }))
})

ws.addEventListener('message', (event) => {
  const frame = JSON.parse(event.data)
  console.log(frame.type, frame)
})
```

Subscribe:

```json theme={null}
{
  "action": "subscribe",
  "topics": [
    "featured",
    "ticker:KXPRESNOMD-28-GN",
    "orderbook:KXPRESNOMD-28-GN",
    "trade:KXPRESNOMD-28-GN",
    "candle:KXPRESNOMD-28-GN:1m"
  ]
}
```

Unsubscribe:

```json theme={null}
{
  "action": "unsubscribe",
  "topics": ["ticker:KXPRESNOMD-28-GN"]
}
```

Legacy single-topic subscribe is also accepted:

```json theme={null}
{ "action": "subscribe", "ticker": "KXPRESNOMD-28-GN" }
```

## WebSocket topics

| Topic                  | Frame type    | Use                                                      |
| ---------------------- | ------------- | -------------------------------------------------------- |
| `featured`             | `featured`    | Top-50 heat-ranked list.                                 |
| `ticker:{ticker}`      | `ticker_info` | Latest price/quote metadata.                             |
| `orderbook:{ticker}`   | `orderbook`   | Sorted book snapshot.                                    |
| `trade:{ticker}`       | `trade`       | Trade prints from venue WS.                              |
| `candle:{ticker}:{tf}` | `candle`      | Candle updates for one timeframe.                        |
| `{ticker}`             | mixed         | Legacy topic with ticker/orderbook/trade/candle fan-out. |

When subscribing to a ticker topic, the server may send initial cached `ticker_info` and `orderbook` frames if available.

## Frame shapes

`featured`:

```json theme={null}
{
  "type": "featured",
  "markets": [],
  "generated_at": 1777553300
}
```

`ticker_info`:

```json theme={null}
{
  "type": "ticker_info",
  "ticker": "KXPRESNOMD-28-GN",
  "venue": "kalshi",
  "title": "Will Gavin Newsom be the Democratic Presidential nominee in 2028?",
  "last": 0.26,
  "bid": 0.25,
  "ask": 0.27,
  "volume24h": 12345,
  "closeTime": 1853884800
}
```

`orderbook`, `trade`, and `candle` use the same shapes as the REST examples above, with a top-level `type`.

## Operational notes

* Public REST callers use `https://data.simplefunctions.dev/v1`; the edge proxy injects the internal origin token.
* Direct origin routes at `https://app.simplefunctions.dev/api/data/v1/*` are edge-token gated and return `403` without the internal token.
* Anonymous WebSocket connections are capped per IP.
* The server pings WebSocket clients every 30 seconds and closes stale connections after a failed pong.
* Kalshi and Polymarket are normalized into one ticker namespace, but not all fields are available from both venues.
* A non-empty market response and an empty trades response can both be healthy: trades are process-local recent prints, while market metadata can be hydrated from venue REST.
