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

# Market Data

> Real-time crypto prices — REST + a streaming WebSocket.

Rach Market Data gives you the top 250 coins by market cap and live USD prices, served
from Rach's edge cache (no external quota is burned per request). Use the **REST** API for
snapshots and the **WebSocket** for real-time ticks.

<Info>
  Market Data lives at a different base path than the other Payments services:
  `https://api.rach.finance/v1/market/` (note: **no** `/api/v1`). It uses your Payments
  `X-API-Key`; only the health check is public.
</Info>

## REST

### List coins

```bash theme={null}
curl "https://api.rach.finance/v1/market/coins?page=1&limit=50" \
  -H "X-API-Key: $RACH_KEY"
```

```json Response theme={null}
{
  "as_of": 1751584800,
  "total": 250,
  "page": 1,
  "limit": 50,
  "coins": [
    { "symbol": "BTC", "name": "Bitcoin", "price": "62409.00", "rank": "1", "pct_24h": "2.15" }
  ]
}
```

### Batch prices

Up to 100 symbols in one call:

```bash theme={null}
curl "https://api.rach.finance/v1/market/prices?symbols=btc,eth,sol,usdc" \
  -H "X-API-Key: $RACH_KEY"
```

```json Response theme={null}
{ "prices": { "btc": 62409.00, "eth": 1756.68, "sol": 148.32, "usdc": 1.00 } }
```

### Fetch a single coin / asset

Two ways, depending on how much you need — pass the lowercase symbol as the path parameter.

**Just the value** — lightweight, for tickers and totals:

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.rach.finance/v1/market/prices/eth" \
    -H "X-API-Key: $RACH_KEY"
  ```

  ```js Node theme={null}
  const { price } = await (await fetch(
    "https://api.rach.finance/v1/market/prices/eth",
    { headers: { "X-API-Key": process.env.RACH_KEY } },
  )).json();
  ```

  ```python Python theme={null}
  import requests, os
  price = requests.get("https://api.rach.finance/v1/market/prices/eth",
      headers={"X-API-Key": os.environ["RACH_KEY"]}).json()["price"]
  ```
</CodeGroup>

```json Response theme={null}
{ "symbol": "eth", "price": 1756.68 }
```

**Full detail** — price plus rank, 24h change, name and freshness:

```bash theme={null}
curl "https://api.rach.finance/v1/market/coins/btc" -H "X-API-Key: $RACH_KEY"
```

```json Response theme={null}
{
  "coin_id": "bitcoin",
  "symbol": "BTC",
  "name": "Bitcoin",
  "price": "62409.00",
  "rank": "1",
  "pct_24h": "2.15",
  "last_updated": "2026-07-04T01:00:00.000Z"
}
```

<Note>
  `GET /v1/market/prices/{symbol}` returns the price as a **number**; `GET /v1/market/coins/{symbol}`
  returns every field as **strings**. A `404` means the symbol isn't tracked.
</Note>

### Health (public)

```bash theme={null}
curl "https://api.rach.finance/v1/market/health"
# { "status": "ok", "fresh": true }   — 503 with "fresh": false if the cache is stale
```

The full REST schemas are in the **Payments API** reference under **Market Data**.

## WebSocket

Connect for real-time price ticks. Browser WebSocket clients can't set headers, so the
API key is passed in the query string:

```
wss://api.rach.finance/v1/market/ws?key=<api-key>
```

### Messages

**Client → server**

```json theme={null}
{ "op": "subscribe", "symbols": ["btc", "eth"] }
{ "op": "subscribe", "symbols": ["*"] }
{ "op": "ping" }
```

**Server → client**

```json theme={null}
{ "op": "snapshot", "as_of": 1751584800, "coins": [ /* CoinMarket objects */ ] }
{ "op": "tick", "as_of": 1751585100, "changes": [
    { "symbol": "btc", "price": 62800.00, "pct_24h": 2.5, "direction": "raise" },
    { "symbol": "eth", "price": 1740.00, "pct_24h": -0.8, "direction": "fall" }
]}
{ "op": "pong" }
```

On connect you receive a `snapshot` of your subscribed symbols, then `tick` messages as
prices change (`direction` is `raise` or `fall`). Send `{ "op": "ping" }` to keep the
connection alive; the server replies `pong`.

### Example

<CodeGroup>
  ```js Browser / Node (ws) theme={null}
  const ws = new WebSocket("wss://api.rach.finance/v1/market/ws?key=" + RACH_KEY);

  ws.onopen = () => {
    ws.send(JSON.stringify({ op: "subscribe", symbols: ["btc", "eth"] }));
  };

  ws.onmessage = (e) => {
    const msg = JSON.parse(e.data);
    if (msg.op === "snapshot") console.log("snapshot", msg.coins);
    if (msg.op === "tick") for (const c of msg.changes) console.log(c.symbol, c.price, c.direction);
  };

  // keep-alive
  setInterval(() => ws.send(JSON.stringify({ op: "ping" })), 30000);
  ```

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

  async def stream(key):
      url = f"wss://api.rach.finance/v1/market/ws?key={key}"
      async with websockets.connect(url) as ws:
          await ws.send(json.dumps({"op": "subscribe", "symbols": ["*"]}))
          async for raw in ws:
              msg = json.loads(raw)
              if msg["op"] == "tick":
                  for c in msg["changes"]:
                      print(c["symbol"], c["price"], c["direction"])

  asyncio.run(stream(RACH_KEY))
  ```
</CodeGroup>

### Limits

<Warning>
  **10 concurrent WebSocket connections per merchant.** Slow consumers have individual ticks
  dropped (they are not disconnected), so always render the latest tick and don't assume you
  receive every one.
</Warning>
