Getting Started
The ByteTree API provides real-time and historical data for FX rates, global stocks, crypto assets, and computed ByteTrend metrics.
Base URL
All endpoints are served from:
https://api.bytetree.io
Authentication
Every endpoint except GET /api/health requires an API key, sent as a bearer token:
curl https://api.bytetree.io/api/assets \
-H "Authorization: Bearer YOUR_API_KEY"
See Authentication for how to obtain a key and the full scope and error model.
Response Format
All endpoints return JSON by default. You can request alternative formats using the format query parameter:
| Format | Content-Type | Description |
|---|---|---|
json | application/json | Default. Standard JSON. |
csv | text/csv | Comma-separated values (RFC 4180). |
toon | text/toon | TOON — compact, token-efficient encoding ideal for LLM consumers. |
# CSV
curl "https://api.bytetree.io/api/rates?format=csv"
# TOON
curl "https://api.bytetree.io/api/rates?format=toon"
Unsupported format values return 400 Bad Request.
Response Conventions
Paginated list envelope
List endpoints that support consumer-driven pagination return:
{
"count": 10,
"total": 4287,
"limit": 10,
"offset": 0,
"data": [ /* records on this page */ ]
}
| Field | Description |
|---|---|
count | Length of the returned data array (records on this page). |
total | Full match count for the same filter, ignoring limit and offset. Computed in parallel with the paged query. |
limit / offset | Echoed input. Consumers paginate by advancing offset until offset + count >= total. |
data | Array of records for this page. |
This is the shape for top-level list endpoints — /api/assets, /api/rates, /api/prices, /api/universes, /api/fx among them.
Compatibility: the envelope is additive. Callers that read only count and data are unaffected. See the Changelog for the cutover note.
Which shape to expect
Rather than memorising a list, read it from the request. Pagination fields appear exactly where you can meaningfully page, and data is an array everywhere except one endpoint:
| Shape | Returns | Where |
|---|---|---|
| Paginated list | {count, total, limit, offset, data[]} | Top-level list endpoints — the envelope above |
| Entity series | {symbol, count, data[]} (or {pair, …}) | One entity’s series, e.g. /api/prices/:symbol, /api/fx/:symbol/series. Not paginated — it’s one thing’s history, not a list |
| Keyed map | {count, data} where data is an object keyed by symbol | /api/metrics/latest only. count is the number of keys |
The keyed map is the one to watch: data is an object, not an array, so iterating it as a list fails rather than returning nothing.
Single-record reads
Single-record endpoints — e.g. GET /api/assets/:id, GET /api/rates/:pair — return { data: ... } and may include the lookup key as a sibling field. They are not paginated.
Unknown query parameters are rejected
Passing a parameter an endpoint doesn’t accept returns 400, rather than being silently ignored:
{ "error": "Unknown query parameter 'typ'. Did you mean 'type'? Accepted parameters: type, region, sector, limit, offset." }
Close misspellings get a suggestion, and the accepted list is always echoed. An endpoint that takes no parameters says so explicitly.
This is deliberate. Silently ignoring an unrecognised parameter means a filter you thought you applied simply didn’t apply, and the response looks plausible — a filtered query returning unfiltered data with a 200. Failing loudly turns a wrong answer into an obvious error.
format is accepted everywhere and never counts as unknown. Authentication is checked first, so an invalid key returns 401 rather than 400, even if the request also carries a bad parameter.
Available Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/health | Health check |
GET | /api/currencies | Currency code to name mapping |
GET | /api/rates | Latest rates for all FX pairs |
GET | /api/rates/sparklines | 12-month weekly closes per pair |
GET | /api/rates/:pair | Daily time series for one pair |
GET | /api/assets | List assets with optional filters |
GET | /api/assets/:id | Single asset by ID or symbol |
GET | /api/prices | Latest price per symbol |
GET | /api/prices/sparklines | 12-month weekly closes per symbol |
GET | /api/prices/:symbol | Daily price time series for one symbol |
GET | /api/fx | Tracked FX pairs with latest scores and trend regime |
GET | /api/fx/:symbol | Latest computed metrics for one FX pair |
GET | /api/fx/:symbol/series | Daily metric time series for one FX pair |
GET | /api/fx/:symbol/prices | Close-only price series for one FX pair |
GET | /api/metrics/latest | Latest metrics across all symbols |
GET | /api/metrics/:symbol/latest | Latest computed metrics snapshot |
GET | /api/metrics/:symbol | Computed metrics time series |
GET | /api/metrics/distribution | Cross-sectional metric distributions |
GET | /api/metrics/regime-transitions | Trend-regime transition event log |
GET | /api/summary | Single-screen dataset overview |
GET | /api/universes | Available universes and dimension configs |
GET | /api/universes/:universe | Latest group metrics across all dimensions |
GET | /api/universes/:universe/:category | Groups within a dimension |
GET | /api/universes/:universe/:category/:group | Time series for one group |
GET | /api/universes/:universe/:category/config | Grouping config for a dimension |
:universe takes a slug your key can reach. Examples throughout this documentation use macro, but your key’s universes depend on your subscription — list them with GET /api/universes and substitute one of yours. A universe outside your scope returns 403; see Universes.
Data Sources
All market data is sourced from institutional-grade data providers and updated daily. FX pairs are quoted as XXXUSD (e.g. GBPUSD = 1.27 means 1 GBP buys 1.27 USD).
Quick Example
Fetch the latest rate for GBP/USD:
curl https://api.bytetree.io/api/rates/GBPUSD
{
"pair": "GBPUSD",
"count": 365,
"data": [
{
"date": "2025-02-17T00:00:00.000Z",
"open": 1.2580,
"high": 1.2620,
"low": 1.2560,
"close": 1.2601
}
]
}