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


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