adona-robot — market data by capacity

Quickstart

Three steps, one key, about five minutes. The free trial serves EURUSD, XAUUSD with 7 days of history, which is all these steps need.

1. Open an account and get the key

Sign up with your company address and confirm it from the mail we send. On your account page, start the 7-day trial: the key is shown once, then only its fingerprint is kept. Save it on your server.

Open an account

2. Your server: the key for a token, then history

The key stays on your server. Your server trades it for a token per end user (the end_user_id is yours to choose), and the token reads candles. With curl, replace adk_live_... with your key and the token placeholder with the access_token it returns; the Python and JavaScript read the key from the ADONA_API_KEY environment variable.

# your server exchanges the key for a token
curl -s https://api.adona-robot.com/v1/tokens \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer adk_live_...' \
  -d '{"end_user_id":"u-42"}'

# the token reads candles
curl -s 'https://api.adona-robot.com/v1/candles?symbol=EURUSD&timeframe=M1&limit=1000' \
  -H 'Authorization: Bearer <token>'
# pip install requests
import os

import requests

API = "https://api.adona-robot.com"

# your server exchanges the key for a token
r = requests.post(
    f"{API}/v1/tokens",
    headers={"Authorization": f"Bearer {os.environ['ADONA_API_KEY']}"},
    json={"end_user_id": "u-42"},
    timeout=30,
)
if not r.ok:
    raise SystemExit(f"refused: {r.status_code} {r.text}")
token = r.json()["access_token"]

# the token reads candles
r = requests.get(
    f"{API}/v1/candles",
    headers={"Authorization": f"Bearer {token}"},
    params={"symbol": "EURUSD", "timeframe": "M1", "limit": 1000},
    timeout=30,
)
if not r.ok:
    raise SystemExit(f"refused: {r.status_code} {r.text}")
page = r.json()
print(len(page["candles"]), "bars, newest:", page["candles"][-1] if page["candles"] else None)
// Node 18+, on your server: ADONA_API_KEY=... node history.mjs
const API = "https://api.adona-robot.com";

async function call(path, bearer, init = {}) {
  const response = await fetch(`${API}${path}`, {
    ...init,
    headers: { Authorization: `Bearer ${bearer}`, "Content-Type": "application/json" },
  });
  if (!response.ok) throw new Error(`refused: ${response.status} ${await response.text()}`);
  return response.json();
}

const key = process.env.ADONA_API_KEY;
if (!key) throw new Error("set ADONA_API_KEY to your API key");

// your server exchanges the key for a token
const { access_token: token, expires_in } = await call("/v1/tokens", key, {
  method: "POST",
  body: JSON.stringify({ end_user_id: "u-42" }),
});

// the token reads candles
const page = await call("/v1/candles?symbol=EURUSD&timeframe=M1&limit=1000", token);
console.log(page.candles.length, "bars, newest:", page.candles.at(-1));
console.log(`token ok, expires in ${expires_in} s`);

The answer is a page of candles, oldest first, with next_before to read further back. The token's expires_in says how long it lasts, in seconds: reuse it until then.

The same on a Node server, with the token reused until it expires: token-server.ts

3. The live stream

The token buys a ticket, and the ticket opens the stream. The ticket travels as a second subprotocol, never in the URL, and expires within seconds, so open the stream right after. curl gets the ticket; holding the stream open takes a WebSocket client, which the Python and JavaScript tabs are. The JavaScript runs in a browser, which may call the API only from a page whose origin your account declares: add it under the browser origins of your account page, then paste this in the console of that page, with your token. Prices arrive as quote frames, bars as candle frames.

# a ticket for the stream, from the token
curl -s -X POST https://api.adona-robot.com/v1/ws-ticket \
  -H 'Authorization: Bearer <token>'

# the stream itself takes a WebSocket client: see Python or JavaScript
# pip install requests "websockets>=13"
import asyncio
import json
import os

import requests
from websockets.asyncio.client import connect

API = "https://api.adona-robot.com"
STREAM = "wss://api.adona-robot.com/v1/stream"


def post(path, bearer, body=None):
    r = requests.post(API + path, headers={"Authorization": f"Bearer {bearer}"}, json=body, timeout=30)
    if not r.ok:
        raise SystemExit(f"refused: {r.status_code} {r.text}")
    return r.json()


async def main():
    token = post("/v1/tokens", os.environ["ADONA_API_KEY"], {"end_user_id": "u-42"})["access_token"]
    # a ticket for the stream, from the token
    ticket = post("/v1/ws-ticket", token)["ticket"]
    # the stream: live quotes and one-minute bars
    async with connect(STREAM, subprotocols=["adona.data.v1", f"ticket.{ticket}"]) as ws:
        await ws.send(json.dumps({
            "type": "subscribe", "request_id": "sub",
            "payload": {"channels": ["quote:EURUSD", "candle:EURUSD:M1"]},
        }))
        async for message in ws:
            print(json.loads(message))


asyncio.run(main())
// a ticket for the stream, from the token
const { ticket } = await (await fetch("https://api.adona-robot.com/v1/ws-ticket", {
  method: "POST",
  headers: { Authorization: "Bearer <token>" },
})).json();

// the stream: live quotes and one-minute bars
const ws = new WebSocket("wss://api.adona-robot.com/v1/stream", ["adona.data.v1", `ticket.${ticket}`]);
ws.onopen = () => ws.send(JSON.stringify({
  type: "subscribe", request_id: "sub", payload: { channels: ["quote:EURUSD", "candle:EURUSD:M1"] },
}));
ws.onmessage = (event) => console.log(JSON.parse(event.data));

Declare an origin on your account page

A complete browser client, with reconnects and the refusals that retrying cannot change: browser.ts

Next

Every route, field and refusal code is in the reference. The account page shows your usage, your key id, its rotation and the way to a paid plan.

API reference · Your account · The plans