Skip to content

Market Data Integration Guide

Step-by-step guide to fetching live cryptocurrency prices and streaming real-time updates.

Prerequisites

  • A Rach API key (live_sk_… for production, test_sk_… for sandbox)
  • Basic HTTP knowledge for REST endpoints
  • WebSocket experience for the streaming section

Base URL

https://api.rach.finance/v1/market

Authentication

Pass your API key on every REST request via header:

bash
X-API-Key: live_sk_YOUR_KEY

For WebSocket connections use the key query parameter (browsers cannot set custom headers during WebSocket upgrade):

wss://api.rach.finance/v1/market/ws?key=live_sk_YOUR_KEY

REST Endpoints

Health Check

Check whether market data is live. No authentication required.

bash
curl https://api.rach.finance/v1/market/health
json
{
  "status": "ok",
  "fresh": true
}

status is "stale" (HTTP 503) when the cached data has not been refreshed within the expected window.


List All Coins

Returns a paginated list of all tracked coins ordered by market-cap rank. Each coin includes full market data.

GET /v1/market/coins?page=1&limit=50
Query paramDefaultMaxDescription
page11-based page number
limit50250Results per page
bash
curl https://api.rach.finance/v1/market/coins \
  -H "X-API-Key: live_sk_YOUR_KEY"
javascript
const res = await fetch('https://api.rach.finance/v1/market/coins', {
  headers: { 'X-API-Key': 'live_sk_YOUR_KEY' }
})
const { coins, total, page, limit, as_of } = await res.json()

console.log(`Showing ${coins.length} of ${total} coins`)
console.log(`Data as of: ${new Date(as_of * 1000).toISOString()}`)
coins.forEach(c => console.log(`${c.symbol.toUpperCase()}: $${c.current_price}`))
python
import requests

res = requests.get(
    'https://api.rach.finance/v1/market/coins',
    headers={'X-API-Key': 'live_sk_YOUR_KEY'}
)
data = res.json()

for coin in data['coins']:
    print(f"{coin['symbol'].upper()}: ${coin['current_price']:,.2f}")
go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
)

type Coin struct {
    Symbol       string  `json:"symbol"`
    Name         string  `json:"name"`
    CurrentPrice float64 `json:"current_price"`
    MarketCapRank int    `json:"market_cap_rank"`
}

type CoinsResponse struct {
    Coins []Coin `json:"coins"`
    Total int    `json:"total"`
    AsOf  int64  `json:"as_of"`
}

func main() {
    req, _ := http.NewRequest("GET", "https://api.rach.finance/v1/market/coins", nil)
    req.Header.Set("X-API-Key", "live_sk_YOUR_KEY")

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()

    var data CoinsResponse
    json.NewDecoder(resp.Body).Decode(&data)

    for _, c := range data.Coins {
        fmt.Printf("#%d %s (%s): $%.2f\n",
            c.MarketCapRank, c.Name, c.Symbol, c.CurrentPrice)
    }
}

Response:

json
{
  "as_of": 1751672738,
  "total": 100,
  "page": 1,
  "limit": 50,
  "coins": [
    {
      "id": "bitcoin",
      "symbol": "btc",
      "name": "Bitcoin",
      "image": "https://cdn.rach.finance/coins/bitcoin.png",
      "current_price": 62150.43,
      "market_cap": 1221043294823,
      "market_cap_rank": 1,
      "total_volume": 28430192837,
      "high_24h": 62500.00,
      "low_24h": 61400.00,
      "price_change_24h": 430.21,
      "price_change_percentage_24h": 0.697,
      "price_change_percentage_1h_in_currency": 0.12,
      "price_change_percentage_7d_in_currency": -2.34,
      "circulating_supply": 19700000,
      "total_supply": 21000000,
      "max_supply": 21000000,
      "ath": 108786.00,
      "ath_change_percentage": -42.85,
      "ath_date": "2024-12-17T15:02:41.429Z",
      "atl": 67.81,
      "atl_change_percentage": 91621.84,
      "atl_date": "2013-07-06T00:00:00.000Z",
      "last_updated": "2026-07-04T23:40:02.315Z"
    }
  ]
}

Get Single Coin Detail

Returns the full market data object for one coin. Use the symbol (e.g. btc, eth, sol).

GET /v1/market/coins/:symbol
bash
curl https://api.rach.finance/v1/market/coins/btc \
  -H "X-API-Key: live_sk_YOUR_KEY"
javascript
const res = await fetch('https://api.rach.finance/v1/market/coins/btc', {
  headers: { 'X-API-Key': 'live_sk_YOUR_KEY' }
})
const coin = await res.json()
console.log(`${coin.name}: $${coin.current_price}`)
python
res = requests.get(
    'https://api.rach.finance/v1/market/coins/btc',
    headers={'X-API-Key': 'live_sk_YOUR_KEY'}
)
coin = res.json()
print(f"{coin['name']}: ${coin['current_price']:,.2f}")

Response: A single CoinMarket object (same fields as in the list above).


Get Prices for Multiple Coins

Lightweight endpoint for looking up prices without full market data. Accepts up to 100 symbols per request.

GET /v1/market/prices?symbols=btc,eth,sol,bnb
bash
curl "https://api.rach.finance/v1/market/prices?symbols=btc,eth,sol" \
  -H "X-API-Key: live_sk_YOUR_KEY"
javascript
const symbols = ['btc', 'eth', 'sol', 'bnb', 'usdt']
const res = await fetch(
  `https://api.rach.finance/v1/market/prices?symbols=${symbols.join(',')}`,
  { headers: { 'X-API-Key': 'live_sk_YOUR_KEY' } }
)
const { prices } = await res.json()
// prices = { btc: 62150.43, eth: 3412.88, sol: 148.20, ... }
python
res = requests.get(
    'https://api.rach.finance/v1/market/prices',
    params={'symbols': 'btc,eth,sol,bnb'},
    headers={'X-API-Key': 'live_sk_YOUR_KEY'}
)
prices = res.json()['prices']
for symbol, price in prices.items():
    print(f"{symbol.upper()}: ${price:,.2f}")

Response:

json
{
  "prices": {
    "btc": 62150.43,
    "eth": 3412.88,
    "sol": 148.20,
    "bnb": 594.71
  }
}

Get Single Coin Price

GET /v1/market/prices/:symbol
bash
curl https://api.rach.finance/v1/market/prices/btc \
  -H "X-API-Key: live_sk_YOUR_KEY"
json
{
  "symbol": "btc",
  "price": 62150.43
}

WebSocket Streaming

The WebSocket API delivers price updates in real time. After connecting and subscribing, you immediately receive a full snapshot, then lightweight tick messages whenever prices change.

Connection

wss://api.rach.finance/v1/market/ws?key=live_sk_YOUR_KEY

The key is validated on connection — an invalid or missing key closes the connection with HTTP 401 before the upgrade completes.

Message Flow

1. Connect  →  wss://api.rach.finance/v1/market/ws?key=…
2. Send     →  {"op":"subscribe","symbols":["btc","eth"]}
3. Receive  ←  {"op":"snapshot","as_of":…,"coins":[…]}    ← immediate full data
4. Receive  ←  {"op":"tick","as_of":…,"changes":[…]}      ← on every price change

Client → Server Messages

Subscribe to specific coins:

json
{ "op": "subscribe", "symbols": ["btc", "eth", "sol"] }

Subscribe to all tracked coins:

json
{ "op": "subscribe", "symbols": ["*"] }

You can re-subscribe at any time to change your coin filter. The server sends a new snapshot immediately on each subscribe.

Application-level ping (optional):

json
{ "op": "ping" }

Response: { "op": "pong" }. The server also sends WebSocket protocol ping frames every 45 seconds — most clients handle pong automatically.

Server → Client Messages

snapshot — sent immediately after subscribe, contains full coin data for your subscribed symbols:

json
{
  "op": "snapshot",
  "as_of": 1751672738,
  "coins": [
    {
      "id": "bitcoin",
      "symbol": "btc",
      "name": "Bitcoin",
      "current_price": 62150.43,
      "market_cap": 1221043294823,
      "market_cap_rank": 1,
      "price_change_percentage_24h": 0.697,
      "high_24h": 62500.00,
      "low_24h": 61400.00,
      "...": "all CoinMarket fields"
    }
  ]
}

tick — sent after each poll when prices changed. Only coins that moved are included:

json
{
  "op": "tick",
  "as_of": 1751672978,
  "changes": [
    { "symbol": "btc", "price": 62210.15, "pct_24h": 0.78, "direction": "raise" },
    { "symbol": "eth", "price": 3398.44, "pct_24h": -0.22, "direction": "fall" }
  ]
}

direction is "raise" when the price increased since the last poll, "fall" when it decreased.

pong — response to a client ping:

json
{ "op": "pong" }

Integration Examples

JavaScript / Browser

javascript
class RachMarketData {
  constructor(apiKey) {
    this.apiKey = apiKey
    this.ws = null
    this.reconnectDelay = 1000
    this.prices = new Map()
  }

  connect(symbols = ['*'], onSnapshot, onTick) {
    this.ws = new WebSocket(
      `wss://api.rach.finance/v1/market/ws?key=${this.apiKey}`
    )

    this.ws.onopen = () => {
      console.log('Connected to Rach market data')
      this.reconnectDelay = 1000
      this.ws.send(JSON.stringify({ op: 'subscribe', symbols }))
    }

    this.ws.onmessage = (event) => {
      const msg = JSON.parse(event.data)

      if (msg.op === 'snapshot') {
        // Full baseline — store all prices locally
        msg.coins.forEach(c => this.prices.set(c.symbol, c.current_price))
        onSnapshot?.(msg.coins, msg.as_of)

      } else if (msg.op === 'tick') {
        // Delta — update only changed coins
        msg.changes.forEach(c => this.prices.set(c.symbol, c.price))
        onTick?.(msg.changes, msg.as_of)
      }
    }

    this.ws.onerror = (err) => console.error('WebSocket error', err)

    this.ws.onclose = () => {
      console.log(`Disconnected. Reconnecting in ${this.reconnectDelay}ms…`)
      setTimeout(() => this.connect(symbols, onSnapshot, onTick), this.reconnectDelay)
      this.reconnectDelay = Math.min(this.reconnectDelay * 2, 30_000)
    }
  }

  getPrice(symbol) {
    return this.prices.get(symbol.toLowerCase())
  }

  disconnect() {
    this.ws?.close()
  }
}

// Usage
const market = new RachMarketData('live_sk_YOUR_KEY')

market.connect(
  ['btc', 'eth', 'bnb', 'sol'],
  (coins, asOf) => {
    console.log('Snapshot received:', coins.length, 'coins')
    coins.forEach(c => updatePriceUI(c.symbol, c.current_price))
  },
  (changes, asOf) => {
    changes.forEach(c => {
      updatePriceUI(c.symbol, c.price)
      console.log(`${c.symbol} ${c.direction === 'raise' ? '▲' : '▼'} $${c.price}`)
    })
  }
)

React Hook

javascript
import { useState, useEffect, useRef, useCallback } from 'react'

function useMarketData(apiKey, symbols = ['*']) {
  const [prices, setPrices] = useState({})
  const [coins, setCoins] = useState([])
  const [connected, setConnected] = useState(false)
  const wsRef = useRef(null)

  useEffect(() => {
    let reconnectTimer
    let delay = 1000

    function connect() {
      const ws = new WebSocket(
        `wss://api.rach.finance/v1/market/ws?key=${apiKey}`
      )
      wsRef.current = ws

      ws.onopen = () => {
        setConnected(true)
        delay = 1000
        ws.send(JSON.stringify({ op: 'subscribe', symbols }))
      }

      ws.onmessage = (e) => {
        const msg = JSON.parse(e.data)
        if (msg.op === 'snapshot') {
          setCoins(msg.coins)
          setPrices(Object.fromEntries(msg.coins.map(c => [c.symbol, c.current_price])))
        } else if (msg.op === 'tick') {
          setPrices(prev => {
            const next = { ...prev }
            msg.changes.forEach(c => { next[c.symbol] = c.price })
            return next
          })
        }
      }

      ws.onclose = () => {
        setConnected(false)
        reconnectTimer = setTimeout(() => {
          delay = Math.min(delay * 2, 30_000)
          connect()
        }, delay)
      }
    }

    connect()
    return () => {
      clearTimeout(reconnectTimer)
      wsRef.current?.close()
    }
  }, [apiKey])

  const getPrice = useCallback((symbol) => prices[symbol.toLowerCase()], [prices])

  return { coins, prices, connected, getPrice }
}

// Usage
function PriceTicker() {
  const { coins, connected, getPrice } = useMarketData('live_sk_YOUR_KEY', ['btc', 'eth', 'sol'])

  return (
    <div>
      <p>Status: {connected ? '● Live' : '○ Reconnecting…'}</p>
      {coins.map(coin => (
        <div key={coin.symbol}>
          <img src={coin.image} width={20} alt={coin.name} />
          <strong>{coin.name}</strong>
          <span>${getPrice(coin.symbol)?.toLocaleString()}</span>
          <span style={{ color: coin.price_change_percentage_24h >= 0 ? 'green' : 'red' }}>
            {coin.price_change_percentage_24h?.toFixed(2)}%
          </span>
        </div>
      ))}
    </div>
  )
}

Python (asyncio)

python
import asyncio
import json
import websockets

async def stream_prices(api_key: str, symbols: list[str]):
    url = f"wss://api.rach.finance/v1/market/ws?key={api_key}"
    prices = {}

    async with websockets.connect(url) as ws:
        print("Connected")

        # Subscribe
        await ws.send(json.dumps({"op": "subscribe", "symbols": symbols}))

        async for raw in ws:
            msg = json.loads(raw)

            if msg["op"] == "snapshot":
                for coin in msg["coins"]:
                    prices[coin["symbol"]] = coin["current_price"]
                print(f"Snapshot: {len(msg['coins'])} coins loaded")

            elif msg["op"] == "tick":
                for change in msg["changes"]:
                    prices[change["symbol"]] = change["price"]
                    arrow = "▲" if change["direction"] == "raise" else "▼"
                    print(f"{change['symbol'].upper()} {arrow} ${change['price']:,.2f}")

asyncio.run(stream_prices("live_sk_YOUR_KEY", ["btc", "eth", "sol"]))

Node.js (REST — one-shot price check)

javascript
import https from 'node:https'

async function getPrices(symbols, apiKey) {
  const url = `https://api.rach.finance/v1/market/prices?symbols=${symbols.join(',')}`
  const res = await fetch(url, { headers: { 'X-API-Key': apiKey } })

  if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
  const { prices } = await res.json()
  return prices
}

// Usage
const prices = await getPrices(['btc', 'eth', 'usdt'], 'live_sk_YOUR_KEY')
console.log(`BTC: $${prices.btc.toLocaleString()}`)
console.log(`ETH: $${prices.eth.toLocaleString()}`)

Displaying Price Direction

The direction field in tick messages makes it easy to colour price movements in your UI:

javascript
// Map direction to styles
const style = {
  raise: { color: '#16a34a', symbol: '▲' },  // green
  fall:  { color: '#dc2626', symbol: '▼' },  // red
}

market.connect(['*'], null, (changes) => {
  changes.forEach(({ symbol, price, direction }) => {
    const el = document.getElementById(`price-${symbol}`)
    if (!el) return
    const { color, symbol: arrow } = style[direction]
    el.textContent = `${arrow} $${price.toLocaleString()}`
    el.style.color = color
  })
})

Error Handling

REST

javascript
async function fetchWithRetry(url, options, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      const res = await fetch(url, options)
      if (res.status === 401) throw new Error('Invalid API key')
      if (res.status === 404) return null          // coin not found
      if (res.status === 503) throw new Error('Market data temporarily unavailable')
      if (!res.ok) throw new Error(`HTTP ${res.status}`)
      return await res.json()
    } catch (err) {
      if (i === retries - 1) throw err
      await new Promise(r => setTimeout(r, 500 * 2 ** i))
    }
  }
}

WebSocket Reconnection

The examples above already implement exponential backoff reconnection (1s → 2s → 4s → … → 30s). A few additional tips:

  • Re-send the subscribe message on every reconnect inside onopen
  • Store the latest snapshot in memory so your UI isn't blank during a brief reconnect
  • Check fresh from /v1/market/health before displaying data if you need to indicate staleness

Best Practices

Use WebSocket for real-time display — REST endpoints are for initial page loads or server-side lookups. The WebSocket stream has no rate limits and pushes updates automatically.

Use /v1/market/prices for bulk price-only lookups — it's cheaper than /v1/market/coins when you only need numbers, not full metadata.

Apply the snapshot as your baseline — on connect, the server sends a full snapshot for your subscribed coins. Apply it to your local state immediately, then patch it with each subsequent tick.

Handle reconnects gracefully — network blips are normal. The reconnect pattern in the examples above keeps your UI live automatically.


Testing with wscat

bash
npm install -g wscat

wscat -c "wss://api.rach.finance/v1/market/ws?key=live_sk_YOUR_KEY"

# Once connected, type:
{"op":"subscribe","symbols":["btc","eth","sol"]}
# → snapshot arrives immediately
# → ticks arrive whenever prices move

Next Steps

Rach Payments API