API Reference
All /v1/* routes require an X-API-Key header matching API_KEY. Health routes
are unauthenticated.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/stream/ticks?symbols=EURUSD,XAUUSD | SSE stream of live bid/ask. symbols optional; omit for all. |
| GET | /v1/market-data/tick?symbol=EURUSD | Latest cached quote. |
| GET | /v1/market-data/candles?symbol=EURUSD&timeframe=H1&count=200 | Closed trendbars. |
| GET | /v1/symbols | Resolved catalog: symbolId, digits, enabled. |
| GET | /health/live | Process is up. |
| GET | /health/ready | 200 when connected and ticks are fresh, else 503 with details. |
SSE ticks
curl -N -H 'X-API-Key: …' 'localhost:8010/v1/stream/ticks?symbols=EURUSD'
event: tick
data: {"symbol":"EURUSD","bid":1.08532,"ask":1.08545,"spread":0.00013,"ts":"…Z","provider":"ctrader"}The stream replays the last known tick for each requested symbol on connect, so a
client joining mid-session does not wait for the next quote on a quiet instrument.
It also emits status events on connection state changes, carrying a dropped
counter — see Architecture.
Candle shape
GET /v1/market-data/candles returns candles stamped at the END of their UTC
interval, matching lookup-trader’s app/providers/base.py::Candle field for
field:
{"ts": "2026-08-08T14:00:00Z", "open": 1.0853, "high": 1.0861, "low": 1.0849,
"close": 1.0857, "volume": 4210.0, "provider": "ctrader",
"source_instrument": "EURUSD", "spread": null, "spread_source": null}cTrader sends utcTimestampInMinutes as the interval start; the conversion
happens server-side in decode.py. Only closed bars are returned — the
currently-forming bar is dropped.
/health/ready returns 503 until the broker session is connected and a quote
has arrived within TICK_STALENESS_SECONDS. Over a market close it can stay
ready with "reason": "no quotes received yet" — no ticks is normal there; only
staleness after quotes have been flowing is a fault.