API guide

Crypto price API: aggregated real-time prices, bars and streams

Bitcoin does not have one price. It has hundreds, one per venue and quote currency, and the API you pick decides which of them your users see.

On this page
  1. Aggregated vs single-venue crypto prices
  2. USDT is not USD: the stablecoin basis
  3. Crypto never closes: 24/7 bars on UTC days
  4. Real-time crypto prices over WebSocket
  5. How to choose the best crypto API for your app
  6. Questions

Key takeaways

  • A crypto price API returns bid, ask and last trade for pairs such as BTCUSD, plus OHLCV bars and a real-time stream.
  • Aggregated prices combine many venues into one quote; a single-venue price inherits that venue's outages and wicks.
  • USDT is not USD: on 28 September 2026 USDT traded at about 0.99955 dollars, 4.5 basis points below par.
  • Crypto bars run 24/7 on UTC days; in one September week BTCUSD's median hourly range on Saturday was a third of Monday's.
  • On crypto quotes `ts` is when the aggregation received the quote; on trades it is the trade's own event time.

A crypto price API gives your code the current price of a coin pair such as BTCUSD over HTTP: best bid, best ask and last trade, each with a timestamp. Good ones add OHLCV bars and a WebSocket stream of real-time crypto prices. The decision that matters most is not the shape of the endpoint but whose price it returns: one venue's, or an aggregate of many.

This pillar of our market data API series explains aggregation, the stablecoin basis, 24/7 bars and streaming, with code tested against the live API on 28 September 2026. TickerLayer publishes aggregated prices for 2,500+ crypto pairs over REST and WebSocket. Start with one request:

Latest BTCUSD quoteShell
curl -sS "https://api.tickerlayer.com/crypto/quote/BTCUSD" \
  -H "x-api-key: $TICKERLAYER_API_KEY"

GET /crypto/quote/BTCUSD

{
  "symbol": "BTCUSD",1
  "bid": 82820,2
  "ask": 82820.01,
  "bid_size": 8.15315,3
  "ask_size": 3.0544,
  "timestamp": 17905911129684
}
  1. symbolBase and quote with no separator: bitcoin priced in US dollars.
  2. bidBest aggregated bid. The ask is one cent higher: on the busiest pairs the spread sits at the minimum price step.
  3. bid_sizeSize available at the bid, in coins.
  4. timestampUnix milliseconds. On crypto quotes this is when the aggregation received the quote.
Captured live at 10:25 UTC on 28 September 2026. REST numerics are JSON numbers.

The same key and response shape cover the rest of the surface: /crypto/trade/last/{symbol} for the latest print, /crypto/snapshot/{symbol} for bid, ask, last trade and the day's change in one call, and /crypto/agg/... for bars. GET /crypto/symbols returns the enabled symbol list with names.

JobCallWhy
Price tile with daily changeGET /crypto/snapshot/{symbol}Bid, ask, last trade, prev_close and change in one request
Portfolio value every few minutesGET /crypto/quote/{symbol}Value at the mid, and keep the spread for context
Chart or backtestGET /crypto/agg/{symbol}/...OHLCV bars from 1 minute to 1 day, paginated
Ticker, alerts, botscrypto.quotes and crypto.tradesEvery change pushed to you on one socket
Pick the endpoint by job. Doing the stream's job by polling REST is the quickest way to burn a monthly quota.

Aggregated vs single-venue crypto prices

Every crypto venue runs its own order book. At any moment their best prices differ by a few basis points, and by far more when one of them is thin, halted, or quoting in a different stablecoin. A single-venue API gives you one of those books faithfully, outages and wicks included. A cryptocurrency data aggregator combines many of them into one quote per pair.

FeatureSingle-venue feedAggregated feed
What the price meansWhere that venue would fill youA consensus across venues
One venue goes downYour price stops or jumpsThe others keep the quote alive
Thin-book wicksPassed straight throughDiluted by deeper books
Executable as shownYes, on that venueNo: a reference price
Best forTrading on that venueDisplay, valuation, alerts, analytics
Neither is better in general. They answer different questions.

TickerLayer is the second kind: one aggregated quote per pair, built from the deepest global liquidity, with no venue named in the payload. We compare it with regulated fiat-dollar venues that sit outside the aggregation and publish the result, basis included, on the crypto data-quality page. If you execute on a specific venue, use that venue's own feed for order decisions; an aggregated price is for everything around the trade.

USDT is not USD: the stablecoin basis

Much of the world's crypto liquidity is not quoted in dollars at all. It is quoted in dollar stablecoins, tokens designed to trade at one dollar. Designed to, not guaranteed to: they drift a few basis points either side of par on a normal day, and further under stress. A price in USDT is a price in a token, and turning it into dollars takes one more multiplication.

  • 0.99953 / 0.99956USDTUSD bid / ask
  • −4.5 bpsUSDT against one dollar, at the mid
  • 1.0003 / 1.0004USDCUSD bid / ask
  • +3.5 bpsUSDC against one dollar, at the mid
GET /crypto/quote/USDTUSD and /crypto/quote/USDCUSD, captured at 10:49 UTC on 28 September 2026.

price in USD = price in USDT × USDTUSDbasis (bps) = (USDTUSD − 1) × 10,000

USDTUSD
Dollars per USDT: the stablecoin's own mid price.
basis
How far the stablecoin sits from par. Negative means below a dollar.
82,900 USDT × 0.999545 = 82,862.28 USD: 37.72 dollars, or 4.5 basis points, below the USDT figure.

An aggregate that blends stablecoin-quoted and dollar-quoted books therefore carries a small signed basis against venues that quote only in dollars. We report it as a number instead of hiding it, and you can measure it yourself, because the stablecoins are ordinary symbols on the same endpoint: USDTUSD and USDCUSD. During issuer or banking stress, major stablecoins have briefly traded several percent below a dollar, so a converter that hard-codes 1.0 fails silently at exactly the wrong moment.

The same basis runs through perpetual futures, which are priced in stablecoin terms. It is one reason the gap between a coin and its perpetual is never only a funding story; our perpetual futures guide separates the two with live numbers.

Crypto never closes: 24/7 bars on UTC days

Stock bars follow a session; crypto bars follow the clock. GET /crypto/agg/{symbol}/{multiplier}/{timespan}/{from}/{to} returns 1, 5 or 15-minute, 1 or 4-hour, or daily bars, and a daily bar is simply a UTC day, midnight to midnight, weekends included. The snapshot's prev_close is the close of the last completed UTC day: on Monday morning BTCUSD's was 84,472, Sunday's close.

BTCUSD daily bars, Saturday 19 to Sunday 27 September 2026

Nine UTC days, both weekends included. Monday the 21st rose 6.7% from open to close.TickerLayer GET /crypto/agg/BTCUSD/1/day/2026-09-19/2026-09-27, captured 28 September 2026.

Weekends are in the series, but they do not behave like weekdays. Hourly bars for one week make the point, and the script is a reusable pattern for any bar request: follow next_offset until it is null.

weekend.pyPython
import os
from collections import defaultdict
from datetime import datetime, timezone

import requests

BASE_URL = "https://api.tickerlayer.com"
HEADERS = {"x-api-key": os.environ["TICKERLAYER_API_KEY"]}


def bars(symbol, multiplier, timespan, start, end):
    """All bars in a UTC date range, following next_offset until it is null."""
    path = f"{BASE_URL}/crypto/agg/{symbol}/{multiplier}/{timespan}/{start}/{end}"
    params = {"sort": "asc", "limit": 5000, "offset": 0}
    out = []
    while True:
        resp = requests.get(path, params=params, headers=HEADERS, timeout=15)
        resp.raise_for_status()
        body = resp.json()
        out.extend(body["results"])
        if body.get("next_offset") is None:
            return out
        params["offset"] = body["next_offset"]


# Hourly high-low range in basis points, grouped by UTC weekday.
ranges = defaultdict(list)
for bar in bars("BTCUSD", 1, "hour", "2026-09-21", "2026-09-27"):
    weekday = datetime.fromtimestamp(bar["t"] / 1000, tz=timezone.utc).strftime("%a")
    ranges[weekday].append((bar["h"] - bar["l"]) / bar["o"] * 10_000)

for weekday in ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]:
    r = sorted(ranges[weekday])
    print(f"{weekday}  {len(r)} bars  median hourly range {r[len(r) // 2]:5.1f} bps")
Output
Mon  24 bars  median hourly range  63.5 bps
Tue  24 bars  median hourly range  52.5 bps
Wed  24 bars  median hourly range  54.3 bps
Thu  24 bars  median hourly range  52.4 bps
Fri  24 bars  median hourly range  44.7 bps
Sat  24 bars  median hourly range  21.4 bps
Sun  24 bars  median hourly range  31.3 bps

BTCUSD median hourly range by UTC weekday

  • Mon63.5
  • Tue52.5
  • Wed54.3
  • Thu52.4
  • Fri44.7
  • Sat21.4
  • Sun31.3
One week is an example, not a law. Measure your own pairs before you tune anything to it.TickerLayer hourly bars, 21 to 27 September 2026.

In that week a typical Saturday hour moved a third as much as a Monday hour. If you run volatility alerts or position sizing on crypto, a single threshold for the whole week will be too loose on weekends or too twitchy on weekdays; key it to the hour of the week instead. Long ranges of minute bars take several requests, because each interval has a maximum span per request (31 days for 1-minute bars); the crypto docs list them.

Real-time crypto prices over WebSocket

Polling one quote a second for 20 pairs costs 1.7 million requests a day. A stream costs one connection. Connect to wss://stream.tickerlayer.com/?apiKey=..., wait for the ready frame, then subscribe to crypto.quotes and crypto.trades. This Node script reconnects with backoff and prints the mid, the spread and each trade:

crypto-stream.mjsJavaScript
// npm install ws
import WebSocket from "ws";

const URL = "wss://stream.tickerlayer.com/?apiKey=" +
  encodeURIComponent(process.env.TICKERLAYER_API_KEY);

const SUBSCRIBE = {
  action: "subscribe",
  channels: ["crypto.quotes", "crypto.trades"],
  symbols: ["BTCUSD", "ETHUSD"],
};

let backoffMs = 1000;

function onFrame(msg) {
  if (msg.type === "quote") {
    // Crypto numerics arrive as strings: convert before doing arithmetic.
    const bid = Number(msg.bid);
    const ask = Number(msg.ask);
    const mid = (bid + ask) / 2;
    const bps = ((ask - bid) / mid) * 10_000;
    console.log(`${msg.symbol} quote  mid ${mid.toFixed(2)}  spread ${bps.toFixed(3)} bps`);
  } else if (msg.type === "trade") {
    const at = new Date(msg.ts).toISOString().slice(11, 23);
    console.log(`${msg.symbol} trade  ${Number(msg.price)} x ${Number(msg.size)}  at ${at} UTC`);
  } else if (msg.type === "system" || msg.type === "error") {
    console.log(msg.type, msg.event, msg.code ?? "");
  }
}

function connect() {
  // The stream sends uncompressed frames; keep compression off on the client too.
  const ws = new WebSocket(URL, { perMessageDeflate: false });

  ws.on("message", (data) => {
    const msg = JSON.parse(data.toString());
    if (msg.type === "system" && msg.event === "ready") {
      backoffMs = 1000;
      ws.send(JSON.stringify(SUBSCRIBE));
    }
    onFrame(msg);
  });

  ws.on("close", (code) => {
    const wait = backoffMs + (Math.random() * backoffMs) / 2;
    console.log(`closed with ${code}, reconnecting in ${Math.round(wait)} ms`);
    backoffMs = Math.min(backoffMs * 2, 30_000);
    setTimeout(connect, wait);
  });

  ws.on("error", (err) => console.error("socket error:", err.message));
}

connect();
Output (first frames, 10:55 UTC)
system ready
system subscribed
ETHUSD quote  mid 2655.73  spread 0.038 bps
BTCUSD trade  82921.32 x 7e-8  at 10:55:31.946 UTC
ETHUSD trade  2654.43 x 0.01940123  at 10:55:31.998 UTC
ETHUSD trade  2655.56 x 0.021283  at 10:55:31.955 UTC
BTCUSD trade  82921.32 x 0.02  at 10:55:32.076 UTC
BTCUSD quote  mid 82968.01  spread 0.001 bps
BTCUSD trade  82968.01 x 0.00006  at 10:55:32.052 UTC
BTCUSD trade  82970.7 x 0.340013  at 10:55:32.582 UTC

A crypto.quotes frame

{
  "type": "quote",1
  "channel": "crypto.quotes",
  "asset": "crypto",
  "symbol": "BTCUSD",
  "bid": "82849.99",2
  "ask": "82850",
  "bid_size": "15.50507",
  "ask_size": "1.87429",
  "ts": 17905912038363
}
  1. typeSwitch on it: quote, trade, system or error.
  2. bidA string on the stream. Parse with Number() or a decimal type, never by slicing text.
  3. tsReceipt time on quotes. On trade frames, ts is the trade's own event time.
Captured live on 28 September 2026. Tiny sizes can arrive in exponent form, such as "3e-8".
  • Measure delay on trades, not quotes. Quote ts is when the aggregation received the quote, so now - ts on a quote tells you little about the market. Trade ts is the event time of the print.
  • Expect interleaving. Trades from many venues arrive with their own event times, so consecutive frames can be a few milliseconds out of order (10:55:31.998, then 10:55:31.955 above) and a few basis points apart. Sort by ts if you rebuild a tape.
  • Ignore dust for display. A last-price line drawn through a 7e-8 BTC print jumps for no economic reason. Chart the mid or bars, or filter prints by size.
  • Keep the handler fast. Parse, update state, return. Hand heavy work to a queue so a busy minute never backs up the socket.

Reconnects, heartbeats and snapshot behaviour are covered in depth in the WebSocket market data guide. If you plan to act on this stream, the Python trading bot tutorial builds a paper-trading loop on top of it, and the Discord price bot sends alerts from the same stream to a chat channel.

How to choose the best crypto API for your app

Questions to ask any crypto API

  • Whose price is it: one venue, or an aggregate? Is that stated?
  • Which quote currency: USD, USDT or a blend? Is the basis measured?
  • Bid and ask with sizes, not only a last price.
  • Bars on documented UTC boundaries, weekends included, with pagination.
  • A stream with a ready handshake, snapshots and documented error frames.
  • Readable limits: X-RateLimit-Limit on every response and a 429 with Retry-After.
  • Stocks, FX and commodities on the same key, if your app will ever show them next to coins.

Ask, too, how the price is checked. Our crypto benchmark compares the aggregate with venues that are not part of it and publishes the method next to the numbers; any provider you shortlist should be able to show you the same kind of evidence.

A free crypto API is fine for a prototype. Before you ship, read the licence: many free tiers forbid commercial display, and redistribution is usually a separate right. The TickerLayer free tier includes 3,000 REST requests a month with no card; WebSocket comes with paid plans, and free accounts can request a trial from the dashboard.

Crypto feedPriceREST requestsWebSocketUse
Free$03,000 a monthTrial on requestPrototyping
Individual$49 a month250,000 a month1 connection, 10 symbolsPersonal and research
Business$449 a month25,000,000 a month10 connections, unlimited symbolsCommercial use
Yearly billing takes 20% off, and three or more feeds take 15% off the cart. Per-second limits differ by plan: read X-RateLimit-Limit.

Symbol lists and sample responses are on the crypto data API product page, and pricing has the bundles for stocks, forex and perpetuals on the same key.

Questions

Is there a free crypto price API?

Yes. The TickerLayer free tier includes 3,000 REST requests a month with no card. Streaming comes with paid plans, and free accounts can request a WebSocket trial from the dashboard.

What is the best crypto API for real-time prices?

The one whose price definition matches your use. For display, alerts and valuation, an aggregated feed with bid, ask and a stated quote currency is the safer default; for executing on one venue, use that venue's own feed.

Why is BTCUSDT different from BTCUSD?

BTCUSDT is priced in the USDT stablecoin and BTCUSD in US dollars. USDT trades a few basis points either side of one dollar, so the two prices differ by that basis before any venue differences.

Do crypto daily candles have a close if the market never closes?

Yes. A daily bar is a UTC day, midnight to midnight, and its close is the last price before 00:00 UTC. Weekends are full days in the series, with their own bars.

What is a cryptocurrency data aggregator?

A service that combines quotes and trades from many venues into one price per pair, so a single venue's outage or thin book does not become your price.

How many crypto pairs does TickerLayer cover?

2,500+ pairs across the aggregation layer. GET /crypto/symbols returns the enabled symbol list with names, and the same key works for forex, stocks and commodities once those feeds are on the account.

Keep reading

Ready to integrate?

Start with the free tier, explore the docs, and connect via REST or WebSocket in minutes.