A forex one-minute history API
One-minute OHLC bars by REST, up to 5 000 a page, paged back with a cursor: what a backtest or a chart reads, through the metered API rather than a file.
- How far back
- Since 16.05.2025 for EURUSD, GBPUSD, USDJPY, XAUUSD; since 17.03.2026 or 17.05.2026 for the others, each symbol's page giving its own date. The trial reads the last 7 days, on EURUSD · XAUUSD.
- Paging back
- Ask for M1 bars with a limit of up to 5 000. Each page comes oldest first with next_before, the cursor to the page before it, which is null when there is nothing older for your account. The newest page ends with the bar still forming. Prices are strings, exactly as served.
- What a bar is made of
- Before 21.05.2026 (22.05.2026 for EURUSD), the one-minute history comes from a different price feed than the live series, and so do some one-minute bars filled in afterwards, until 04.06.2026 at the latest; /symbols.json gives each symbol's dates. The volume is a tick volume: the number of price updates in the bar; bars from the other feed, on any timeframe until 05.06.2026 at the latest, carry its tick volume, not counted the same way.
- What it costs
- Billed in slices of 1 000 bars, rounded up per response, so ask for what you need: a day of one-minute bars (1 440 at most) read in one response is 2 slices, and an empty page costs nothing. A page that carries the bar still forming counts as following the symbol live: past 80% of its open hours in a month, that symbol adds CHF 150. Plans from CHF 500 a month.
Facts as of 03.10.2026.
The code: one-minute history, paged back
# pip install requests
# One-minute history, paged back with the cursor: ADONA_API_KEY=... python history_m1.py EURUSD 3
import os
import sys
from datetime import datetime, timedelta, timezone
import requests
API = "https://api.adona-robot.com"
symbol = sys.argv[1] if len(sys.argv) > 1 else "EURUSD"
days = int(sys.argv[2]) if len(sys.argv) > 2 else 3
def get(path, bearer, **kwargs):
r = requests.request(kwargs.pop("method", "GET"), API + path, headers={"Authorization": f"Bearer {bearer}"}, timeout=30, **kwargs)
if not r.ok:
raise SystemExit(f"refused: {r.status_code} {r.text}")
return r.json()
# your server exchanges the key for a token
token = get("/v1/tokens", os.environ["ADONA_API_KEY"], method="POST", json={"end_user_id": "u-42"})["access_token"]
# pages of up to 5000 bars, newest first; next_before is the page before.
# Every bar served is billed, so each page asks only for the minutes it can still need.
start = datetime.now(timezone.utc) - timedelta(days=days)
since = start.strftime("%Y-%m-%dT%H:%M:%SZ")
bars, before, until = [], None, datetime.now(timezone.utc)
while True:
minutes = int((until - start).total_seconds() // 60) + 1
params = {"symbol": symbol, "timeframe": "M1", "limit": max(1, min(5000, minutes))}
if before:
params["before"] = before
page = get("/v1/candles", token, params=params)
if before is None and page["candles"]:
page["candles"].pop() # the newest bar is still forming: not history yet
bars = page["candles"] + bars
before = page["next_before"]
if before is None or before <= since: # nothing older, or far enough back
break
until = datetime.fromisoformat(before.replace("Z", "+00:00"))
bars = [b for b in bars if b["time"] >= since]
print(len(bars), "bars from", bars[0]["time"] if bars else None, "to", bars[-1]["time"] if bars else None)
The quickstart: from the key to the live stream, step by step