Connect Your Backend
DefaultDataFeedis 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 theDataFeedinterface 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 APIYou need four capabilities:
| Capability | Used for |
|---|---|
| Symbol search | Symbol picker, watchlists |
| Historical candles | Initial chart load, scroll-back |
| Current price | Watchlist 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.
Step 1 — Symbol search
Request
GET /market-data/search/crypto?symbol=btcResponse
{
"data": [
{
"symbol": "BTCUSDT",
"type": "crypto",
"name": "Bitcoin",
"shortName": "BTC",
"exchange": "binance",
"priceCurrency": "USDT",
"pricePrecision": 2,
"volumePrecision": 6
}
],
"type": "crypto"
}Minimal Express handler:
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=1706745600000from and to are Unix milliseconds. Return only candles inside that window. ChartSpire calls this again when the user scrolls for older data.
Response
{
"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=BTCUSDTResponse
{
"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:
{
"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:
{
"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
unsubscribewith the samedatashape 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
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:
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
/websocketthrough your app server). - Return
Content-Type: application/jsonon HTTP responses. - During local dev, proxy API calls through Vite/webpack to avoid CORS:
// 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
- Open the chart — historical candles should load for the default symbol.
- Scroll left — older candles should append without repeating the same window.
- Watch the last candle — it should update in real time.
- Change symbol — the old stream should stop and the new one should start.
- Run diagnostics:
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
| Symptom | Likely cause |
|---|---|
| Empty chart, no errors | Historical response missing values, or from/to window ignored |
| Chart loads but never updates | WebSocket not sending klines at the subscribed interval, or wrong dataType |
| Duplicate candles on scroll | Backend returns candles outside the requested from/to range |
| Watchlist prices missing | price endpoint missing or wrong response shape |
| Works locally, fails when deployed | CORS, 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
- Default DataFeed — full contract, auth, order book, trades
- Data Access — Binance demo feed and custom
DataFeedinterface - Multi-Chart — multiple charts sharing one data feed