Market data API · Quotes, candles, ticks, stream

Live prices, four ways in.

One symbol or a hundred, a REST poll or a pushed stream, the last tick or the last thousand candles — the same key, the same symbol names and the same JSON envelope across all of it. Every response stamps when the data actually changed.

7 timeframes ~60s data cadence 100 symbols per quote call
tickatlas.com / v1 / quote 200 OK
GET /v1/quote?symbol=EURUSD
EUR/USD
1.08432
1× weight
ask1.08445
spread13 pts
spread_pips1.3
symbolEURUSD
scopequotes
{
  "success": true,
  "data": {
    "symbol": "EURUSD",
    "bid": 1.08432,
    "ask": 1.08445,
    "spread": 13,            // points, never null
    "spread_pips": 1.3,
    "timestamp": "2026-09-18T09:41:07Z"
  }
}
7Timeframes
1,000Candles per call
100Symbols per quote batch
0×Cost of a WebSocket push
Freshness, stated plainly

The data is as new as the last push. Not as new as a cache.

A cache TTL says how long a value may be served before it is re-read. It says nothing about how often the value changes. Those are different numbers here, and only one of them is worth building against.

THE REAL BOUND

Our data updates about every 60 seconds

A batch arrives carrying bid and ask for every configured symbol plus the OHLCV and indicator block for whichever timeframes are due. That interval is the ceiling on how new any number can be, on every surface — REST, stream or history.

  • Bid/ask on every push
  • M1 and M5 blocks every push
  • M15–H1 every 10 min, H4/D1 every 30 min
HOW YOU MEASURE IT

Read the timestamp, don’t assume one

Every payload carries its own age: timestamp on a quote, updated_at on an indicator or screener response, time on a streamed quote. Poll on a change in that value rather than on a fixed interval and you stop paying quota for repeats.

  • timestamp — quote surfaces
  • updated_at — indicator surfaces
  • Unchanged value ⇒ nothing new to process
Cache TTLs are sized to outlast the publish interval for that timeframe, so an entry expires only when our data has stopped updating — which is the signal you want.
Timeframe Published Cache TTL
M1 every 60 s 180 s
M5 every 60 s 180 s
M15 every 600 s 900 s
M30 every 600 s 900 s
H1 every 600 s 900 s
H4 every 1,800 s 2,700 s
D1 every 1,800 s 2,700 s

Spot FX is 24/5, not 24/7. The trading week opens at the Sydney session open on Sunday and closes at the New York session close on Friday; Saturday is always closed. Both boundaries are derived from IANA time zones rather than fixed UTC hours, so they move correctly across daylight-saving changes instead of being an hour wrong for roughly seven months of the year.

Delivery surfaces

Five ways to take the same prices.

Weights are the real usage multipliers, scopes are the permissions your key must carry, and the plan column is the gate enforced inside the route. Pick the cheapest surface that answers your question — the difference between a batched quote and a per-symbol loop is a hundredfold.

Surface Returns Weight Key scope Plan Behaviour worth knowing
GET /v1/quote One symbol, latest bid/ask/spread 1× quotes Every plan Answers the canonical symbol with bid, ask, spread, spread_pips and timestamp.
POST /v1/quotes Up to 100 symbols, chosen fields 1× quotes Every plan One request of quota for the whole batch. A malformed name inside the batch is skipped like a not-found symbol rather than failing the other 99.
GET /v1/ohlc Up to 1,000 completed candles 2× historical Any paid plan Candles carry time, open, high, low, close and volume only. Bid, ask and spread are not on a candle — they are on the quote endpoints.
WS /ws/v1/quotes Pushed bid/ask per subscribed symbol unmetered API key or monitor token Starter, Pro, Enterprise, Monitor Not billed on any plan: credits_per_push is 0 everywhere. Access and the symbol ceiling are what the plan buys — 5 symbols on Starter, 20 on Pro, unlimited on Enterprise.
GET /v1/ticks Raw bid/ask ticks, 1-hour window 3× historical Pro and Enterprise only from and to are both required and the range is capped at one hour. The row cap is a hard LIMIT 50000 in the query, not a parameter you can raise.
Field reference

Everything a quote can carry.

The envelope is { "success": true, "data": { … } }. All six fields are always present.

Field Type What it carries
symbol string Canonical symbol, always.
bid number Bid, from our data.
ask number Ask, from the same quote.
spread number Spread in POINTS. Coerced to 0 rather than null when it is missing, so the field is always a number.
spread_pips number The same spread converted to pips using that symbol’s digit count. null when the point spread is 0 — a zero spread is not converted.
timestamp string When our data last updated this symbol. This is the freshness figure that matters, not a cache age.

A candle is not a quote. /v1/ohlc returns data.candles — an array of { time, open, high, low, close, volume } — plus count and retention. There is no bars key, and no bid, ask or spread on a candle.

Request pattern

One header. Three shapes.

A key in X-API-Key and a symbol is a working call. Everything else is choosing the surface that matches the question — and, for a poller, watching the timestamp instead of the clock.

  • Batch quotes cost 1× for up to 100 symbols.
  • fields trims the payload, not the price.
  • limit on candles is 1 to 1000, validated not clamped.
  • Responses carry the canonical symbol, e.g. EURUSD.
# one symbol
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://tickatlas.com/v1/quote?symbol=EURUSD"

# up to 100 symbols for ONE request of quota - pick your fields
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"symbols":["EURUSD","GBPUSD","XAUUSD"],"fields":["bid","ask","spread_pips"]}' \
  "https://tickatlas.com/v1/quotes"

# completed candles - the key is "candles", not "bars"
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://tickatlas.com/v1/ohlc?symbol=EURUSD&timeframe=H1&limit=10"
# poll on the INGEST cadence, not on a cache TTL.
# each quote's timestamp tells you when our data last updated it;
# polling faster than our data updates spends quota for a repeat.
import time, requests

H   = { "X-API-Key": KEY }
URL = "https://tickatlas.com/v1/quotes"
seen = None

while True:
    d = requests.post(URL, headers=H, json={"symbols": WATCHLIST}).json()["data"]
    latest = max((q["timestamp"] for q in d["quotes"] if q.get("timestamp")), default=None)
    if latest != seen:
        seen = latest
        handle(d["quotes"])          # d["count"] rows, d["not_found"] names the rest
    time.sleep(30)                 # our data updates about every 60 s
spread_pips
1.3 EUR/USD
spread 13 pts
Price pairbid · ask
Unitsspread in points, spread_pips in pips
Freshnesstimestamp (ISO 8601)
Two numbers, two meanings

Points are what the quote carries. Pips are what you compare.

spread is the raw point value from our data, and it depends on that symbol’s digit count — 13 points on a five-digit pair is not 13 points on a three-digit one. spread_pips is the same spread normalised through the symbol’s digits, which is the figure that means the same thing across instruments.

Push, not poll

/ws/v1/quotes — live, and free to run.

Four client actions, seven server message types, and no quota accounting anywhere in the path. Streaming is unmetered on every plan that can reach it; what the plan buys is access and the number of symbols you may hold open at once.

// 1. connect, then authenticate within 10 seconds
→ {"action": "auth", "key": "tk_…"}
← {"type": "authenticated", "plan": "pro", "max_symbols": "20",
     "credits_per_push": 0, "max_connections": 2}

// 2. subscribe by canonical name
→ {"action": "subscribe", "symbols": ["EURUSD", "XAUUSD"]}
← {"type": "subscribed", …}

// 3. one message per symbol per update, about every 60 s
← {"type": "quote", "symbol": "EURUSD", "bid": 1.08432,
     "ask": 1.08445, "volume": 1184, "time": "2026-09-18T09:41:07Z"}

// 4. keepalive every 30 s, or send {"action":"ping"} yourself
← {"type": "heartbeat", …}
Simultaneous symbols per connection, and at most two connections per account.
Plan Stream Symbols Cost per push
Free / Trial / Demono——
Pay-as-you-gono——
Starteryes50
Monitoryes100
Proyes200
Enterpriseyesunlimited0

Unmetered since 2026-07-30. Pushes used to be billed per symbol per update, which meant a dashboard left open quietly drained a daily quota through a channel that writes no usage log line. The per-push charge is now zero on every plan and billing happens only on explicit REST calls. Push activity is still counted, in a separate unbilled counter shown on your dashboard.

Timeframes

7 intervals, validated the same way everywhere.

M1, M5, M15, M30, H1, H4, D1, defaulting to H1. The same allow-list guards /v1/ohlc, /v1/indicator, /v1/multi and /v1/summary, and anything outside it is a 400 carrying the valid set — never a silent fallback. W1 is accepted by /v1/heatmap alone; nothing above D1 is accepted anywhere else.

  • M1 — 1 Minute
  • M5 — 5 Minutes
  • M15 — 15 Minutes
  • M30 — 30 Minutes
  • H1 — 1 Hour (default)
  • H4 — 4 Hours
  • D1 — Daily

History depth is set per timeframe, never per plan. Every tier reads the same window, and each /v1/ohlc response repeats it back in data.retention — so the live figure is always one call away rather than something to trust from a page. See the full schedule.

Developer-first behavior

One canonical symbol, on every response.

Every surface answers under the instrument's canonical name, and each one makes a deliberate choice about how its data is shaped.

NAMES

Canonical in, canonical out

Send EURUSD; the response echoes the canonical name.

  • Canonical names out
SERIES

One row per timestamp

Candle and tick queries keep one row per timestamp, so a series never repeats a timestamp and limit is not quietly divided.

  • Deterministic row per timestamp
INGEST

Outliers rejected per record

Each incoming record is checked against that symbol’s last cached mid, with a percentage threshold per asset class and a separate guard on an absurd spread. A rejected record is skipped; the rest of the batch lands.

  • FX 5% · gold 15% · oil 25% · crypto 50%
GAPS

Holes found on a 2-minute scan

A background job walks stored candles, learns each symbol’s own trading hours from its history, and queues a backfill for anything missing — so a closed market is not mistaken for a gap.

  • Learned profile, no hardcoded calendar
Developer API pricing

Quotes on every plan.

Quotes work on every plan. Candles (/v1/ohlc) need a paid plan, and pay-as-you-go counts as paid for endpoint access. The two gates worth planning around are the WebSocket, which starts at Starter, and tick history, which is Pro and Enterprise only.

There is no free tier and no self-serve trial. Every account starts on pay-as-you-go with $2.50 of prepaid credit, no card and no overage — monthly plans lift the starting quota.
Pay as you go
$0 to start 200 requests/day · 30/min until you top up
  • $2.50 of credit included
  • Every REST endpoint except raw ticks
  • No card required, no overage
  • 10 API keys
Start free
Starter
$29/mo 10,000 requests · 120/min
  • Every REST endpoint except raw ticks
  • WebSocket streaming, 5 symbols
  • Released calendar actuals
  • Email support
  • 3 API keys
View plan
Enterprise
$349/mo 1,000,000 requests · 6000/min
  • Everything in Pro
  • Dedicated support
  • 100 API keys
  • Custom indicators
  • SLA guarantee
  • On-premise option
Contact sales
Common software patterns

Cheap surface for the frequent work.

The shape that keeps recurring: take the expensive, wide payload once, then keep the moving edge fresh from the cheapest surface that carries it.

Price tickers and boards

One POST /v1/quotes covers a hundred-symbol board for a single request of quota, and every row carries its own timestamp so a stale instrument is visible rather than silently frozen.

boardstickerswatchlists

Charts with a live last bar

Draw the history from /v1/ohlc once, then keep only the right-hand edge moving from the WebSocket. The candle endpoint is 2× and the stream is free, so the cheap surface carries the frequent work.

chartingdashboards

Cost-aware execution checks

spread and spread_pips answer "what would this trade cost right now" without a second call.

pre-trade checksrouting
Frequently asked questions

Market data, clarified.

How fresh the numbers really are, what the stream costs, what a candle does and does not carry, and whether a quote is ever an average.

How fresh is the data, really?

As fresh as the last update of our data, which arrives about every 60 seconds covering every configured symbol and timeframe. Every response tells you exactly when that was — timestamp on a quote, updated_at on an indicator payload — so you never have to infer freshness from a cache figure. Cache TTLs exist to survive a gap in our data, not to describe how new the numbers are: they are 180 seconds on M1 and M5, 900 on M15 through H1 and 2,700 on H4 and D1, deliberately longer than the interval at which each timeframe is published.

Is there a WebSocket, or only polling?

Both. /ws/v1/quotes is live and pushes a quote message per subscribed symbol whenever our data updates. It is available on Starter, Pro, Enterprise and the Monitor plan, and it is unmetered — credits_per_push is 0 on every plan, so a tab left open all day costs nothing against your quota. The ceiling is the simultaneous symbol count: 5 on Starter, 10 on Monitor, 20 on Pro, unlimited on Enterprise, with at most two concurrent connections per account.

Do candles include bid, ask or spread?

No. A candle from /v1/ohlc is time, open, high, low, close and volume. Bid, ask and spread live on /v1/quote and POST /v1/quotes for the live picture, and on /v1/spread for the statistics. Joining them is a timestamp join you do client-side.

Why is the market quiet at the weekend?

Because spot FX is a 24/5 market, not 24/7. The week opens at the Sydney session open on Sunday and closes at the New York session close on Friday, with Saturday always closed — and both boundaries are derived from IANA time zones, so they shift correctly with daylight saving instead of drifting by an hour for seven months of the year.

Is a quote ever an average?

No. bid, ask and spread are one quote from our data, never an average, and candle and tick history keep one row per timestamp.

How many symbols are there?

Coverage follows our data, so a fixed number on a marketing page would be stale the next time a symbol is added. GET /v1/symbols returns the live list with pagination up to 500 rows a page and filters for forex, commodities, indices, crypto and stocks, and GET /v1/symbols/{symbol} returns the detail for one.

What stops a bad price getting through?

Every incoming record is compared against that symbol’s own last cached mid and skipped if the move exceeds the threshold for its asset class — 5% for FX majors, 15% for gold, 25% for oil, 10% for indices, 50% for the large crypto pairs — plus a separate rejection for a quoted spread wider than 10% of mid. It is an outlier filter on ingest, and the thresholds are deliberately loose: they exist to catch broken data, not to smooth real volatility.

What about missing candles?

A background scan walks price_data every two minutes, learns each symbol’s own trading profile from its history rather than assuming a schedule, and writes any hole it finds to a backfill queue. Because the trading profile is learned, a market that is legitimately closed is not reported as a gap.

What do these calls cost against my quota?

A quote is 1× whether it is one symbol or a hundred in a batch. A candle request is 2×. A tick request is 3× and is Pro or Enterprise only. WebSocket pushes are 0×. There is no free tier and no self-serve trial: every account starts on pay-as-you-go with $2.50 of prepaid credit, no card and no overage.

Quotes, candles, ticks and a stream — one key

Stop building the feed.

A hundred symbols for one request of quota, a thousand candles for one (two weighted units), and a push stream that costs nothing to keep open. Start on your $2.50 of credit, no card.