tape · market data API

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.

Authentication

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.

Sources

SourceWhatResolution
dukascopyHistory, downloaded nightly — no data for todaytick
td365Today's ticks (and earlier recorded days) for the selected instrumentstick
tradenationTrade Nation charts: 1m for about the last week, 1h since 2025-01, 1d since 2012 — for instruments without tick history1m / 1h / 1d
tn (live)Trade Nation live feed over WebSocket, pushed as it arrives — not storedtick

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).

Endpoints

GET /api/v1/instruments[?q=]

The catalogue. q filters on key, name and labels.

GET /api/v1/instruments/{key}

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}
}

GET /api/v1/instruments/{key}/{from}/{to}?timeframe=1m&cursor=

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
}

GET /api/v1/instruments/{key}/latest[?timeframe=tick]

Today (UTC) so far — td365 ticks where recorded, else Trade Nation 1m.

GET /api/v1/search?q=

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.

WS /ws — live ticks

// → 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}

GET /api/v1/streams

Health: the live session, every market's status (ok, silent, closed, unsubscribed), last tick age, ticks today; the cost API.

GET /api/v1/series/{source}/{key}/{date} (advanced)

Exactly one source, unstitched: source = dukascopy or scraper (td365), date = YYYY-MM-DD or latest. /api/v1/series?q= lists what each source has.

Conventions

For agents

Start with 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.