One REST API over every tick and bar source we have. One lowercase key per instrument
(dax, gold, aluminium); every other name it is known by is a label
(DAX, GER40, Germany 40) and resolves to the same key. Every response says which source
served which day, and at what resolution.
Every /api/v1 route needs an API key (created in the admin portal):
curl -H "Authorization: Bearer $TAPE_KEY" https://tape.brvo.app/api/v1/instruments
For a quick test in a browser, ?key=tape_… works too. No key or a revoked key → 401.
| Source | What | Resolution |
|---|---|---|
| dukascopy | History, downloaded nightly — no data for today | tick |
| td365 | Today's ticks (and earlier recorded days) for the selected instruments | tick |
| tradenation | Trade Nation charts: 1m for about the last week, 1h since 2025-01, 1d since 2012 — for instruments without tick history | 1m / 1h / 1d |
| tn (live) | Trade Nation live feed over WebSocket, pushed as it arrives — not stored | tick |
Per UTC day the first source with data wins: dukascopy → td365 → tradenation 1m → 1h → 1d.
Ticks carry price (mid) and bid/ask = price ∓ half the Trade Nation spread in force at that
moment (the cost schedule, Europe/London clock).
The catalogue. q filters on key, name and labels.
One instrument (key or any label) with its coverage, the finest resolution available, and the Trade Nation costs (spread windows, sessions, margin).
{
"key": "dax", "name": "Germany 40", "labels": ["DAX","GER40","DEU.IDX/EUR"],
"coverage": [
{"source":"dukascopy","kind":"history","res":"tick","from":"2013-01-01","to":"2026-09-23"},
{"source":"td365","kind":"intraday","res":"tick","from":"2022-02-10","to":"now"},
{"source":"tnbars","kind":"history","res":"1m","from":"2026-09-15","to":"2026-09-24"},
{"source":"tn","kind":"live","res":"tick"}
],
"finest": "tick",
"costs": {"spreads":[{"spread":1,"from":"08:00","to":"16:29"}, …], "sessions":[…], "marginPct":5}
}
Data for UTC days from (inclusive) to to (exclusive), stitched across sources.
timeframe: tick, 10s, 1m, 5m, 15m, 1h, 1d … (default 1m).
{
"key": "aluminium", "requested": "10s", "timeframe": "1m",
"segments": [{"from":"2026-09-22T00:00:00Z","to":"2026-09-24T00:00:00Z","source":"tradenation","res":"1m"}],
"count": 2340,
"bars": [{"t":1790035200000,"o":2610.5,"h":2611.0,"l":2610.1,"c":2610.8,"v":14}, …],
"next": null
}
timeframe.timeframe=tick returns ticks ({t, price, bid, ask}), one UTC day per page; bars come up to 20 000 per page.
When next is not null, call again with &cursor=<next>.Today (UTC) so far — td365 ticks where recorded, else Trade Nation 1m.
Three tiers in order: the registry, Dukascopy markets, and the Trade Nation catalogue.
Every hit carries key — the registry key it is mapped to, or "" when it is not mapped yet.
// → send {"auth":"tape_…"} {"subscribe":["dax","dow"]} // or ["*"] // ← receive, the moment Trade Nation sends it {"type":"tick","key":"dax","t":1790244411034,"price":25283.4,"bid":25282.9,"ask":25283.9}
Health: the live session, every market's status (ok, silent, closed, unsubscribed),
last tick age, ticks today; the cost API.
Exactly one source, unstitched: source = dukascopy or scraper (td365), date = YYYY-MM-DD or
latest. /api/v1/series?q= lists what each source has.
{"error": "…"} with 400 (bad input), 401 (no/invalid key), 404 (unknown instrument).GET /openapi.json (OpenAPI 3.1) — it describes every endpoint and field.
Resolve a name with /api/v1/instruments/{key-or-label}, read finest and coverage to see what exists,
then fetch with /{from}/{to}?timeframe= and follow next until it is null. Always read the served
timeframe, not the one you asked for.