biquote API Reference

Real-time market data API for Forex, Crypto, Metals and Index CFDs — REST endpoints and live WebSocket streaming via SignalR.

Base URL & Authentication

BASE URL https://biquote.io

All REST endpoints are prefixed with /api. The API is publicly accessible — no API key, no signup, no authentication for any read endpoint.

ℹ️
All responses are in JSON format. CORS is open: any origin, any header, no credentials — you can call the API directly from browser code on any site. Admin endpoints (not documented here) require an X-Api-Key header.

Service status: GET /health returns feed and collector health as JSON.

Rate Limits

Polling for live prices? Don't. The WebSocket stream at /hubs/tick pushes every tick to you over one connection, and it does not count against the rate limit at all. It is cheaper for you and for us than any polling loop. If you must poll, poll /api/latest with many symbols at once — never one symbol per request.

Anonymous callers get 15,000 requests per minute per IP in a fixed one-minute window. Exceeding it returns 429 Too Many Requests with these headers:

HeaderMeaning
Retry-AfterSeconds until the window resets (60)
X-RateLimit-LimitYour per-minute request quota
X-RateLimit-RemainingRequests left in the current window (0 on a 429)
X-RateLimit-ResetSeconds until the counter resets

Staying under the limit is easy:

  • Batch — fetch many symbols in one request with /api/latest: ?symbols=EURUSD&symbols=XAUUSD&symbols=BTCUSD (repeat the symbols parameter — the comma form is not supported).
  • Stream — the SignalR hub at /hubs/tick pushes ticks over one connection with no polling and no rate limit.
  • Back off — on a 429, wait the Retry-After seconds before retrying.

Error Handling

The API uses standard HTTP status codes:

CodeMeaning
200Success
400Bad Request — missing or invalid parameters
404Not Found — unknown symbol, or one that has not quoted in over 7 days. Not what a closed market returns
429Too Many Requests — see Rate Limits; honor Retry-After
500Server Error
503Market data store unreachable — an outage on our side, not a closed market. Retry after Retry-After
Error Response
{
  "message": "No tick data available for 'INVALIDXYZ'"
}

Rate-limit rejections and calendar validation errors use an error field instead of message:

429 Response
{
  "error": "rate_limited",
  "message": "Too many requests. Retry after 60 seconds.",
  "hint": "Use GET /api/latest?symbols=... to batch, or stream via SignalR at /hubs/tick.",
  "docs": "https://biquote.io/docs/"
}

Ticks

Access real-time and historical tick data (bid, ask, mid, spread, day range) for any active symbol.

🕒
Markets close, and the response says so. Forex stops quoting from Friday night to Sunday night; equities stop for most of every day. A quote request then returns 200 with the last known price and marketState: "closed" — not an error. Render it as "market closed — last price", and hide the day-change percentage, which belongs to a session that has ended.

Building a trading client? Pass allowStale=false for the strict behaviour: 404 as soon as a quote is over five minutes old.
GET /api/{symbol} Latest tick for a symbol

Returns the most recent tick data for the specified symbol.

Path Parameters
NameTypeRequiredDescription
symbol string required Symbol name (e.g. EURUSD, XAUUSD, BTCUSD)
allowStale boolean optional Default true — a closed market returns its last known price. Pass false to get 404 instead once a quote is over five minutes old.
bash
curl https://biquote.io/api/EURUSD
javascript
const res = await fetch('https://biquote.io/api/EURUSD');
const tick = await res.json();
console.log(tick.bid, tick.ask);
python
import requests
tick = requests.get('https://biquote.io/api/EURUSD').json()
print(tick['bid'], tick['ask'])
json
{
  "symbol": "EURUSD",
  "description": "Euro vs US Dollar",
  "bid": 1.08542,
  "ask": 1.08548,
  "mid": 1.08545,
  "spread": 0.00006,
  "last": 0.0,
  "volume": 0,
  "high": 1.08800,
  "low": 1.08530,
  "direction": "FLAT",
  "dayDiffPercent": -0.09,
  "timestamp": "2026-02-24T10:30:00Z",
  "time": "2026.02.24 10:30:00",
  "source": "MetaTrader 5 (Broker 1)",
  "marketState": "open",
  "stale": false,
  "quoteAgeSeconds": 0,
  "lastQuoteAt": "2026-02-24T10:30:00Z"
}
⚠️
last and volume are always 0 for FX/CFD sources — use mid for a single price.

marketState, stale and quoteAgeSeconds are on every response, live or not — so freshness is never something you have to infer from a field being absent. The same verdict is on the X-Quote-State and X-Quote-Age-Seconds headers.

time is MetaTrader's native format and is not reliably parseable by new Date() across browsers — parse timestamp.
GET /api/latest Latest ticks for multiple symbols

Returns the latest tick for multiple symbols in a single request.

Query Parameters
NameTypeRequiredDescription
symbols string[] required One or more symbol names. Repeat parameter for multiple: ?symbols=EURUSD&symbols=XAUUSD
bash
curl "https://biquote.io/api/latest?symbols=EURUSD&symbols=XAUUSD&symbols=BTCUSD"
GET /api/{symbol}/history Historical tick data

Returns historical tick records for a symbol, newest first.

Parameters
NameTypeRequiredDefaultDescription
symbol string required Symbol name
count integer optional 100 Number of ticks to return (1–1000)
bash
curl "https://biquote.io/api/EURUSD/history?count=50"
GET /api/active List of active symbols with tick data

Returns all symbols that currently have live tick data, including metadata.

bash
curl https://biquote.io/api/active
Response
[
  {
    "name": "EURUSD",
    "description": "Euro vs US Dollar",
    "type": "Forex",
    "exchange": "Forex",
    "source": "MT5"
  },
  {
    "name": "XAUUSD",
    "description": "Gold vs US Dollar",
    "type": "Commodity",
    "exchange": "Forex",
    "source": "MT5"
  }
]

Symbols

Query symbol metadata — name, description, type, exchange, and data source.

GET /api/symbols Get all symbols (with filters)
Query Parameters
NameTypeRequiredDescription
source string optional MT5 or MATRIKS
type string optional Forex, Stock, Crypto, Index, Commodity
exchange string optional Filter by exchange (e.g. Forex)
activeOnly boolean optional Only symbols the broker lists as active (default false)
liveOnly boolean optional Only symbols with live data right now (default false)
⚠️
The catalogue lists ~1,700 instruments but only ~280 quote at any moment. isActive means "the broker lists it" and is true for nearly all of them.

For a picker, a nav menu or any cached list, pass quotedWithinDays=7. liveOnly=true means "quoting this second" and so returns almost nothing at a weekend — a list built on it empties out at exactly the times people browse.
bash
# Symbols with live data right now
curl "https://biquote.io/api/symbols?liveOnly=true"

# All MT5 symbols
curl "https://biquote.io/api/symbols?source=MT5"
GET /api/symbols/search Search symbols by name or description
Query Parameters
NameTypeRequiredDescription
q string required Search query — matches name and description
liveOnly boolean optional Only symbols with live data right now (default false)
limit integer optional Max results (default 25, cap 200)
bash
curl "https://biquote.io/api/symbols/search?q=gold&liveOnly=true"
GET /api/symbols/{name} Get a single symbol by name
bash
curl https://biquote.io/api/symbols/EURUSD

Market Statistics

Top gainers and losers across all asset classes with configurable time periods.

GET /api/market/gainers Top gaining symbols
Query Parameters
NameTypeDefaultDescription
typestringForex, Stock, Crypto, etc.
exchangestringFilter by exchange
limitinteger10Number of results (1–100)
periodstring1DAccepted for compatibility but currently ignored — ranking is always by change since the daily open
bash
# Top 5 forex gainers today
curl "https://biquote.io/api/market/gainers?type=Forex&limit=5&period=1D"
Response
{
  "period": "1D",
  "items": [
    {
      "symbol": "XAUUSD",
      "description": "Gold vs US Dollar",
      "lastPrice": 4396.09,
      "changePercent": 4.25,
      "changeAmount": 179.20,
      "volume": 0
    }
  ]
}
GET /api/market/losers Top losing symbols

Same parameters as /api/market/gainers — returns symbols sorted by largest percentage decline.

bash
curl "https://biquote.io/api/market/losers?type=Crypto&limit=10&period=1H"
GET /api/market/most-active Most active symbols

Same type/exchange/limit filters as /api/market/gainers (no period — ranking is over today's session). Returns the symbols that moved the most today, ranked by relative intraday range (high−low over mid). Traded volume is not available on this feed, so ranking is movement-based.

bash
curl "https://biquote.io/api/market/most-active?limit=10"
GET /api/market/summary Market overview

Overall market snapshot: counts of gaining, losing and unchanged instruments, per asset class.

bash
curl "https://biquote.io/api/market/summary"

News

Financial news feed aggregated from multiple sources. Filter by symbol, language and country.

GET /api/news/market Market & finance news

Returns market and finance related news, aggregated from Google News plus recent well-scored Hacker News stories (about a third of the slots at most), interleaved newest-first. Optionally filter by stock symbol — the Hacker News search follows the filter. Items are distinguishable by their publisher field.

Query Parameters
NameTypeDefaultDescription
symbolstringOptional symbol to filter news (e.g. AAPL, XAUUSD)
languagestringenLanguage code (en, tr, de…)
countrystringUSCountry code (US, TR, GB…)
maxResultsinteger10Number of articles (1–50)
bash
# All market news
curl "https://biquote.io/api/news/market"

# Filtered by symbol
curl "https://biquote.io/api/news/market?symbol=AAPL&maxResults=5"
javascript
const res = await fetch('https://biquote.io/api/news/market?language=tr&country=TR');
const news = await res.json();
news.forEach(a => console.log(a.title, a.publishedDate));
python
import requests
news = requests.get(
    'https://biquote.io/api/news/market',
    params={'language': 'tr', 'country': 'TR', 'maxResults': 10}
).json()
for a in news:
    print(a['title'], a['publishedDate'])
json
[
  {
    "title": "Markets rally as Fed signals rate pause",
    "description": "Wall Street surged on Wednesday after...",
    "url": "https://example.com/article",
    "publisher": "Reuters",
    "publishedDate": "2026-02-24T09:15:00Z",
    "imageUrl": null,
    "category": null,
    "language": "en",
    "country": "US"
  }
]
GET /api/news/hn Hacker News stories

The current Hacker News front page, in rank order — or a relevance-ranked story search when q is given. Same article shape as the other news feeds; description carries the points and comment counts, and Ask HN posts link to their HN discussion since they have no external URL.

Query Parameters
NameTypeDefaultDescription
qstringOptional search keyword (e.g. bitcoin, trading)
maxResultsinteger10Number of stories (1–50)
bash
# Front page
curl "https://biquote.io/api/news/hn"

# Story search
curl "https://biquote.io/api/news/hn?q=bitcoin&maxResults=5"
json
[
  {
    "title": "Show HN: A real-time market data API",
    "description": "342 points · 128 comments",
    "url": "https://example.com/story",
    "publisher": "Hacker News",
    "publishedDate": "2026-08-19T06:30:00Z",
    "imageUrl": null,
    "category": null,
    "language": "en",
    "country": "US"
  }
]

Economic Calendar

Scheduled macroeconomic releases with their actual, forecast and previous readings, across 54 countries. Two feeds sit behind it and their coverage deliberately does not overlap: the MetaTrader 5 economic calendar supplies the twenty-two majors, and a second feed supplies what it omits — Turkey, central and eastern Europe, the smaller euro-area members, Asian emerging markets and the Gulf. Both are normalised to the same shape, so nothing you write needs to know which one a row came from. The store holds a rolling window of roughly 90 days back and 90 days forward.

Call GET /api/calendar/countries for the exact list. A calendar this broad is also a noisy one — filter on importance unless you genuinely want every treasury-bill auction.

GET /api/calendar Releases in a date range

Returns events ordered oldest-first as a bare JSON array.

Query Parameters
NameTypeDefaultDescription
fromISO 8601yesterdayStart of range, UTC
toISO 8601+7 daysEnd of range, UTC
countriesstringComma-separated ISO alpha-2 (US,EU,GB,JP,TR). Omit for all
importancestringMinimum importance: low, medium, high
typestringevent, indicator or holiday
limitinteger200Maximum rows (1–500)
bash
# This week, everything
curl "https://biquote.io/api/calendar"

# High-impact US, EU and Turkish releases only
curl "https://biquote.io/api/calendar?countries=US,EU,TR&importance=high"
javascript
const res = await fetch('https://biquote.io/api/calendar?importance=high&limit=50');
const events = await res.json();
for (const e of events) {
  // actual is null until the figure is published — never treat it as 0
  const status = e.actual === null ? 'upcoming' : `actual ${e.actual}`;
  console.log(e.time, e.countryCode, e.name, status);
}
python
import requests
events = requests.get(
    'https://biquote.io/api/calendar',
    params={'countries': 'US,TR', 'importance': 'high', 'limit': 50}
).json()
for e in events:
    print(e['time'], e['countryCode'], e['name'], e['actual'], e['forecast'])
json
[
  {
    "id": "mql5:900001",
    "eventId": "mql5:840",
    "time": "2026-08-07T12:30:00Z",
    "period": "2026-07-01T00:00:00Z",
    "countryCode": "US",
    "currency": "USD",
    "name": "Nonfarm Payrolls",
    "importance": "high",
    "type": "indicator",
    "sector": "jobs",
    "unit": "job",
    "multiplier": "thousands",
    "digits": 0,
    "actual": 152000,
    "forecast": 145000,
    "previous": 147000,
    "revisedPrevious": 149000,
    "revision": 1,
    "timeMode": "exact",
    "sourceUrl": "https://www.bls.gov/",
    "source": "mql5"
  }
]
Reading the values
FieldWhat it means
actual / forecast / previousnull means not published, not zero. A forthcoming release has no actual; most releases carry no forecast; a holiday carries none of them.
multiplierScale to display, not to multiply out. Payrolls arrives as 152000 with "thousands" — show 152,000K.
unitpercent, currency, job, barrel… how to suffix the number.
timeModeexact, date, notime or tentative. Anything but exact means the clock component is a placeholder.
revisedPreviousSet when the statistics office restated the prior figure. Compare actual against forecast, not against previous.
GET /api/calendar/upcoming Next releases from now

The next scheduled releases, soonest first, over a 30-day horizon.

Query Parameters
NameTypeDefaultDescription
limitinteger20Maximum rows (1–500)
countriesstringComma-separated ISO alpha-2
importancestringMinimum importance
bash
curl "https://biquote.io/api/calendar/upcoming?limit=10&importance=high"
GET /api/calendar/countries Countries carried

Country codes present in the calendar, with names and currencies. Use these values for the countries filter.

json
[
  { "code": "EU", "name": "EU", "currency": "EUR" },
  { "code": "TR", "name": "Türkiye", "currency": "TRY" },
  { "code": "US", "name": "US", "currency": "USD" }
]
GET /api/calendar/{eventId}/history Past prints of one series

Past releases of one recurring series, newest first, so a figure can be read against its own history rather than a single previous print. eventId is the eventId field from any event. Returns 404 when the series is unknown.

Query Parameters
NameTypeDefaultDescription
limitinteger24Maximum rows (1–500)
bash
curl "https://biquote.io/api/calendar/mql5:840/history?limit=12"

OHLC / Candlestick

Retrieve OHLCV candlestick bars for any symbol. Data is sourced from Yahoo Finance (bootstrap), real-time tick aggregation, and MT5 historical feeds. Supports M1 through D1 timeframes with up to 2000 bars per series.

GET /api/{symbol}/ohlc OHLCV candlestick bars

Returns OHLCV bars for the given symbol and timeframe. The most recent (open) bar is prepended with isOpen: true.

Parameters
NameTypeRequiredDefaultDescription
symbol string required Symbol name (e.g. EURUSD, BTCUSD, XAUUSD)
interval string optional 1h Timeframe: 1m 5m 15m 30m 1h 4h 1d
limit integer optional 100 Number of bars to return (1–1000)
from ISO 8601 optional Start of range, e.g. 2026-01-01T00:00:00Z
to ISO 8601 optional End of range
bash
# Latest 100 hourly bars
curl "https://biquote.io/api/EURUSD/ohlc?interval=1h&limit=100"

# Daily bars for a date range
curl "https://biquote.io/api/BTCUSD/ohlc?interval=1d&from=2026-01-01T00:00:00Z"
javascript
const res = await fetch('https://biquote.io/api/EURUSD/ohlc?interval=1h&limit=200');
const data = await res.json();
// data.bars → array of { openTime, open, high, low, close, volume, tickVolume, isOpen }
const closedBars = data.bars.filter(b => !b.isOpen);
console.log(`${data.symbol} ${data.interval}: ${closedBars.length} bars`);
python
import requests
import pandas as pd

r = requests.get('https://biquote.io/api/EURUSD/ohlc', params={'interval': '1h', 'limit': 500})
data = r.json()
df = pd.DataFrame(data['bars'])
df['openTime'] = pd.to_datetime(df['openTime'])
df = df.set_index('openTime')
print(df[['open','high','low','close']].tail())
json
{
  "symbol": "EURUSD",
  "interval": "1h",
  "bars": [
    {
      "openTime": "2026-02-24T13:00:00Z",
      "open": 1.08521,
      "high": 1.08574,
      "low": 1.08498,
      "close": 1.08542,
      "volume": 0,
      "tickVolume": 340,
      "isOpen": true
    },
    {
      "openTime": "2026-02-24T12:00:00Z",
      "open": 1.08480,
      "high": 1.08530,
      "low": 1.08440,
      "close": 1.08521,
      "volume": 0,
      "tickVolume": 412,
      "isOpen": false
    }
  ]
}
📊
Historical bars are available across all supported timeframes. The current open bar (isOpen: true) reflects live tick aggregation and is updated in real time.

SignalR Hub — Real-time Streaming

Connect to the SignalR hub for live tick data. The hub pushes updates as market data arrives — no polling required.

HUB URL https://biquote.io/hubs/tick
ℹ️
Install the SignalR client: npm install @microsoft/signalr or use the CDN: https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.0/signalr.min.js

Client → Server Methods

SEND Subscribe Subscribe to symbol(s)

Subscribe to one or more symbols. You'll receive a ReceiveTick event for each update.

ParameterTypeDescription
symbolsstring[]Array of symbol names
javascript
await connection.invoke('Subscribe', ['EURUSD', 'XAUUSD', 'BTCUSD']);
SEND Unsubscribe Unsubscribe from symbol(s)
javascript
await connection.invoke('Unsubscribe', ['EURUSD']);
INVOKE GetLatestTick Request a single tick
javascript
const tick = await connection.invoke('GetLatestTick', 'EURUSD');
console.log(tick.bid, tick.ask);
INVOKE GetSubscriptions List your current subscriptions
javascript
const symbols = await connection.invoke('GetSubscriptions');
console.log(symbols); // ['EURUSD', 'XAUUSD']

Server → Client Events

ON ReceiveTick Live tick update

Fired whenever a subscribed symbol receives new market data.

javascript
connection.on('ReceiveTick', (tick) => {
  console.log(`${tick.symbol}: bid=${tick.bid} ask=${tick.ask}`);
});

Quick Start

📦
Official clients: npm install biquote (Node/browser, zero dependencies) and pip install biquote (Python). Both handle the feed's gotchas for you — mid as the price, the OHLC envelope, liveOnly filtering.

Full example — connect, subscribe, and receive live prices in the browser.

javascript
import * as signalR from '@microsoft/signalr';

const connection = new signalR.HubConnectionBuilder()
    .withUrl('https://biquote.io/hubs/tick')
    .withAutomaticReconnect()
    .build();

// Listen for live ticks
connection.on('ReceiveTick', (tick) => {
    console.log(`[${tick.symbol}] bid: ${tick.bid}  ask: ${tick.ask}`);
});

// Connect and subscribe
await connection.start();
await connection.invoke('Subscribe', ['EURUSD', 'XAUUSD', 'BTCUSD']);
python
import requests

# REST: Get latest tick
tick = requests.get('https://biquote.io/api/EURUSD').json()
print(f"EURUSD bid={tick['bid']}  ask={tick['ask']}")

# REST: Top gainers today
gainers = requests.get(
    'https://biquote.io/api/market/gainers',
    params={'limit': 5, 'period': '1D'}
).json()
for item in gainers['items']:
    print(f"{item['symbol']}: +{item['changePercent']:.2f}%")
csharp
// Install: dotnet add package Microsoft.AspNetCore.SignalR.Client
using Microsoft.AspNetCore.SignalR.Client;

var connection = new HubConnectionBuilder()
    .WithUrl("https://biquote.io/hubs/tick")
    .WithAutomaticReconnect()
    .Build();

connection.On<object>("ReceiveTick", tick =>
{
    Console.WriteLine($"Tick received: {tick}");
});

await connection.StartAsync();
await connection.InvokeAsync("Subscribe", new[] { "EURUSD", "XAUUSD" });
python
import requests
import pandas as pd

# Fetch 500 hourly EURUSD candles
r = requests.get('https://biquote.io/api/EURUSD/ohlc', params={'interval': '1h', 'limit': 500})
data = r.json()

df = pd.DataFrame(data['bars'])
df['openTime'] = pd.to_datetime(df['openTime'])
df = df.set_index('openTime').sort_index()

# Drop the still-open bar
df = df[~df['isOpen']]

print(df[['open','high','low','close','tickVolume']].tail(10))

# Example: simple moving average
df['sma20'] = df['close'].rolling(20).mean()
print(df[['close','sma20']].tail(5))

Data Models

Tick

  • symbolstringSymbol name (e.g. EURUSD)
  • descriptionstringHuman-readable name
  • bidnumberBest bid price
  • asknumberBest ask price
  • midnumberMidpoint of bid/ask — use this as the price
  • spreadnumberask − bid
  • lastnumberAlways 0 for FX/CFD sources — use mid
  • volumenumberAlways 0 for FX/CFD sources
  • highnumberDay's highest price
  • lownumberDay's lowest price
  • directionstringUP, DOWN or FLAT vs the previous tick
  • dayDiffPercentnumberPercent change since the daily open
  • timestampstring (ISO 8601)Tick timestamp (UTC)
  • sourcestringData source (e.g. MetaTrader 5 (Broker 1))

Symbol

  • namestringSymbol ticker
  • descriptionstringFull name
  • typestringForex / Stock / Crypto / Index / Commodity
  • exchangestringExchange name (e.g. Forex, Crypto)
  • sourcestringData provider: MT5 or MATRIKS

OhlcBar

  • openTimestring (ISO 8601)Bar open timestamp (UTC)
  • opennumberOpening price
  • highnumberHighest price in the bar
  • lownumberLowest price in the bar
  • closenumberClosing price (last tick if bar is open)
  • volumeintegerReal volume (0 for Forex)
  • tickVolumeintegerNumber of ticks in the bar
  • isOpenbooleantrue for the current unfinished bar; updated on every tick