Skip to content

Connect Your Backend

DefaultDataFeed is a reference adapter for local development and evaluation. ChartSpire does not provide or guarantee market data. For applications that depend on live or regulated data, implement the DataFeed interface against your own provider and infrastructure.

A step-by-step recipe to try ChartSpire with DefaultDataFeed against a simple API. For live integrations, implement a custom DataFeed.

For field definitions, see Data Types.
For the full HTTP/WebSocket contract, see Default DataFeed.

What you will build

Browser (ChartSpire)

    ├─ HTTP  → search, historical, price
    └─ WS    → kline stream at chart interval

    Your API

You need four capabilities:

CapabilityUsed for
Symbol searchSymbol picker, watchlists
Historical candlesInitial chart load, scroll-back
Current priceWatchlist quotes
Live klines at chart interval (WebSocket)Real-time chart updates

Live streaming uses the selected chart interval (same as historical). ChartSpire does not rebucket candles on the client.

Request

GET /market-data/search/crypto?symbol=btc

Response

json
{
  "data": [
    {
      "symbol": "BTCUSDT",
      "type": "crypto",
      "name": "Bitcoin",
      "shortName": "BTC",
      "exchange": "binance",
      "priceCurrency": "USDT",
      "pricePrecision": 2,
      "volumePrecision": 6
    }
  ],
  "type": "crypto"
}

Minimal Express handler:

typescript
app.get('/market-data/search/:type', (req, res) => {
  const query = String(req.query.symbol ?? '').toLowerCase()
  const matches = symbols.filter((s) => s.symbol.toLowerCase().includes(query))
  res.json({ data: matches, type: req.params.type })
})

Step 2 — Historical candles

Request

GET /market-data/historical/crypto?symbol=BTCUSDT&interval=1d&from=1704067200000&to=1706745600000

from and to are Unix milliseconds. Return only candles inside that window. ChartSpire calls this again when the user scrolls for older data.

Response

json
{
  "values": [
    {
      "datetime": "2024-01-01T00:00:00.000Z",
      "open": "42000.00",
      "high": "43500.00",
      "low": "41800.00",
      "close": "43200.00",
      "volume": "12845.12"
    }
  ],
  "meta": {
    "symbol": "BTCUSDT",
    "exchange": "binance",
    "original_interval": "1d",
    "mapped_interval": "1d",
    "is_exact_match": true
  },
  "type": "crypto"
}

OHLCV values can be strings or numbers. Timestamps must be consistent with the requested interval.

Supported historical interval values include 1m, 5m, 15m, 30m, 1h, 4h, 1d, 1w, 1M. See Default DataFeed for the full list.

Step 3 — Current price

Request

GET /market-data/price/crypto?symbol=BTCUSDT

Response

json
{
  "price": 43200.5,
  "symbol": "BTCUSDT",
  "timestamp": 1706745600000
}

DefaultDataFeed reads price from the top-level response. Do not nest it under another key.

Step 4 — Live WebSocket (klines)

Connect to your WebSocket endpoint. After connect, ChartSpire sends subscribe messages like:

json
{
  "event": "subscribe",
  "data": {
    "type": "crypto",
    "symbol": "BTCUSDT",
    "dataType": "kline",
    "interval": "1m",
    "subscriptionId": "BTCUSDT_binance_1minute",
    "exchange": "binance",
    "clientId": "client_xxx"
  }
}

Push kline updates as the candle forms. Either shape works:

json
{
  "event": "market_data",
  "data": {
    "symbol": "BTCUSDT",
    "interval": "1m",
    "timestamp": 1706745600000,
    "open": 43100,
    "high": 43250,
    "low": 43080,
    "close": 43200,
    "volume": 12.45
  }
}

Live stream rules

  • Stream klines at the subscribed interval (matches historical and the chart period shown in the UI).
  • Volume should be cumulative within the current candle (exchange-style), not decreasing mid-candle.
  • Send an unsubscribe with the same data shape when ChartSpire removes the last listener.

Order book and trades are optional for a basic chart. They also use WebSocket with dataType: "orderbook" and dataType: "trades".

Step 5 — Wire up the frontend

typescript
import { ChartSpire, DefaultDataFeed, SYMBOL_TYPE } from 'chartspire'
import 'chartspire/dist/chartspire-ui.css'

const dataFeed = new DefaultDataFeed({
  http: {
    baseUrl: 'https://api.example.com',
    endpoints: {
      search: '/market-data/search',
      historical: '/market-data/historical',
      price: '/market-data/price',
    },
  },
  websocket: {
    enabled: true,
    url: 'wss://api.example.com/websocket',
    reconnect: {
      enabled: true,
      initialDelayMs: 1000,
      maxDelayMs: 30000,
      maxAttempts: 5,
    },
  },
})

const chart = new ChartSpire({
  container: document.getElementById('chart-container')!,
  enabledSymbolTypes: [SYMBOL_TYPE.CRYPTO],
  symbol: {
    symbol: 'BTCUSDT',
    type: SYMBOL_TYPE.CRYPTO,
    exchange: 'binance',
  },
  interval: { multiplier: 1, timespan: 'day', text: '1D' },
  dataFeed,
  dataFeedInitialHistoricalBars: 300,
  theme: 'Dark Theme',
})

Use https:// (and wss://) when your API is served over TLS so the browser allows secure WebSocket connections.

If your API requires auth:

typescript
const dataFeed = new DefaultDataFeed({
  http: {
    baseUrl: 'https://api.example.com',
    endpoints: {
      search: '/market-data/search',
      historical: '/market-data/historical',
      price: '/market-data/price',
    },
  },
  websocket: {
    enabled: true,
    url: 'wss://api.example.com/websocket',
  },
  auth: {
    type: 'bearer',
    token: sessionToken,
    applyTo: ['http', 'websocket'],
    websocketMode: 'message',
  },
})

Bearer auth also triggers the WebSocket authenticate handshake documented in Default DataFeed.

CORS and networking checklist

  • Allow your frontend origin on all three HTTP endpoints.
  • Allow WebSocket upgrades from the same origin (or proxy /websocket through your app server).
  • Return Content-Type: application/json on HTTP responses.
  • During local dev, proxy API calls through Vite/webpack to avoid CORS:
typescript
// vite.config.ts (example)
export default defineConfig({
  server: {
    proxy: {
      '/market-data': 'http://localhost:3000',
      '/websocket': { target: 'ws://localhost:3000', ws: true },
    },
  },
})

Then point http.baseUrl at your dev server origin ('' or 'http://localhost:5173').

Verify the integration

  1. Open the chart — historical candles should load for the default symbol.
  2. Scroll left — older candles should append without repeating the same window.
  3. Watch the last candle — it should update in real time.
  4. Change symbol — the old stream should stop and the new one should start.
  5. Run diagnostics:
typescript
dataFeed.debugChannels()
console.log(dataFeed.getActiveSubscriptions())

If subscriptions grow when you switch symbols, check that ChartSpire is calling unsubscribeSymbol with a stable subscriberUID per chart. See Default DataFeed.

Common issues

SymptomLikely cause
Empty chart, no errorsHistorical response missing values, or from/to window ignored
Chart loads but never updatesWebSocket not sending klines at the subscribed interval, or wrong dataType
Duplicate candles on scrollBackend returns candles outside the requested from/to range
Watchlist prices missingprice endpoint missing or wrong response shape
Works locally, fails when deployedCORS, http vs https, or WebSocket blocked by proxy

Beyond testing: custom DataFeed

DefaultDataFeed is a convenience for matching this reference contract during evaluation. For deployed applications, implement the DataFeed interface directly against your provider (search, history, live stream, cleanup). You retain control over reconnect logic, gap recovery, auth, and rate limits.

See Data Access — Implementing custom data feeds.

Next steps