Market holidayJapan will be closed on 2026-11-03 (Culture Day), 5 days from now.All announcements
Article

Market Data API Billing Models: Why WebSocket Streaming Beats REST Polling

Real-time market data is a push workload, not a query workload. We run the numbers on 500 symbols to compare REST polling against WebSocket streaming — and show why per-message billing breaks down at scale.

When teams evaluate a market data API, the first number they look at is usually the unit price: how much per million requests. But for real-time market data, the billing model matters far more than the unit price. The reason is simple — live market data is a push workload, not a query workload. Force a query-based billing model onto a push-based workload, and your costs grow along two axes at once: number of symbols and time.

Two billing models, two very different outcomes

Pricing in this space falls into two buckets:

  • Per-request billing — every HTTP call consumes quota. Well suited to historical pulls, scheduled snapshots, and low-frequency queries.
  • Per-message billing — every tick pushed over a WebSocket is billed. The more symbols you track and the more active the market, the steeper the curve gets.

The problem: many platforms apply per-message billing to streaming too. And live market data generates an astronomically large message volume by nature.

Run the numbers: REST polling for 500 symbols

Say you need to monitor 500 symbols with one-second refresh.

TIQEX's multi-market quote endpoint accepts up to 50 codes per call, so one full polling cycle takes 10 requests:

  • 10 requests/sec × 86,400 sec = 864,000 requests/day
  • Roughly 25.9 million requests/month

What does that mean against plan quotas? Growth allows 500,000 calls per month; Pro allows 5,000,000. Polling 500 symbols every second exceeds even the Pro quota.

The same 500 symbols over WebSocket

  • One WebSocket connection subscribes to all 500 symbols
  • At an average of 2 ticks per symbol per second → 1,000 messages/sec
  • Roughly 2.59 billion messages/month

Now multiply that by your vendor's per-message rate. Even at an aggressive $0.5 per million messages, the monthly bill exceeds $1,300. At $5 per million, it lands at $12,960 — for 500 symbols. Scale to a thousand symbols and the cost structure collapses entirely.

The key point: symbol count is a product requirement. It will not shrink to fit your budget. Per-message billing is therefore a cost structure that only gets worse over time.

Handshake billing: the only structure that stays flat

This is exactly why TIQEX bills the handshake rather than the messages:

  • Establishing a WebSocket connection counts toward your daily quota and per-minute rate limit (RPM)
  • Ticks pushed after the handshake are not metered per message
  • As a result, streaming costs stay nearly constant whether you subscribe to 500 symbols or 5,000

In other words: symbols scale, cost doesn't.

When to use which

ScenarioRecommended modelWhy
Live monitoring, trading terminals, alertingWebSocket streamingContinuous push; flat cost after handshake billing
Historical candle backfill, backtest data prepREST queriesOne-off batch pulls with predictable request counts
Low-frequency snapshots (minute / hourly)REST queriesSmall request volume, simple to implement
Portfolio monitoring across 1,000+ symbolsWebSocket streamingREST polling breaks any quota tier

TIQEX states the principle plainly on its pricing page: Use WebSocket for live ticks; REST for queries and backfill.

Latency: the other overlooked difference

Beyond cost, the two models differ sharply on latency.

REST polling latency has two components: the polling interval plus network round-trip time. One-second polling means an average delay of 500ms (1,000ms worst case) on top of each request's RTT. For minute-scale decisions that is fine. For market making, arbitrage, or real-time alerting, 500ms is enough to invalidate the signal entirely.

WebSocket inverts the direction: the server pushes the moment a quote changes, so latency is governed by the network path alone, with no penalty for waiting on the next poll cycle. TIQEX documents 78ms data latency.

So streaming is not just cheaper — it is faster, and both advantages come from the same design decision.

Verify your own usage profile in Python first

Before picking a plan, work out your real request volume. This script derives monthly consumption from the documented batch limit:

Python
import math

symbols = 500
batch_size = 50        # /api/v1/market/quote accepts up to 50 codes per call
interval_sec = 1       # one-second refresh

requests_per_poll = math.ceil(symbols / batch_size)
per_day = requests_per_poll * (86400 // interval_sec)
per_month = per_day * 30

print(f"requests per poll: {requests_per_poll}")
print(f"requests per day:  {per_day:,}")
print(f"requests/month:    {per_month:,}")
# requests per poll: 10
# requests per day:  864,000
# requests/month:    25,920,000

Swap in your real symbol count and you will immediately see whether REST polling is viable. If your monthly volume already exceeds your plan quota, the right fix is not a bigger plan — it is switching to a streaming subscription.

Migrating from REST polling to streaming: three steps

The migration is simpler than it sounds. In Python, subscribe to the TIQEX streaming endpoint with websockets:

Python
import asyncio, json, os
import websockets

API_KEY = os.environ["TIQEX_API_KEY"]
SYMBOLS = ["BTCUSDT", "ETHUSDT", "AAPL", "EURUSD"]

async def main():
    async with websockets.connect(
        "wss://api.tiqex.io/api/v1/market/stream",
        additional_headers={"X-API-Key": API_KEY},   # use extra_headers on websockets < 14
    ) as ws:
        await ws.send(json.dumps({
            "action": "subscribe",     # first message must be subscribe
            "market": "crypto",
            "symbols": SYMBOLS,
        }))
        async for raw in ws:
            frame = json.loads(raw)
            if not frame.get("succ"):
                print("error:", frame.get("code"), frame.get("msg"))
                continue
            data = frame["data"]
            if data.get("type") == "subscribed":
                print("subscribed:", data.get("symbols"))
            elif data.get("type") == "tick":
                tick = data["tick"]
                print(tick["symbol"], tick["last"], tick["at"])

asyncio.run(main())

Three things matter here:

  1. The first message must be subscribe, carrying market and symbols. Symbol count is capped by your plan.
  2. You receive a type=subscribed acknowledgement frame first, then a continuous stream of type=tick frames. Production code should wait for the acknowledgement before processing business logic.
  3. Authentication uses the X-API-Key header (or Authorization: Bearer). WebSocket and REST share the same key.

The full subscription protocol, field definitions, and error codes live in the WebSocket streaming docs; for the semantic differences between the two styles, see polling vs streaming compared.

Migration checklist

  1. Confirm plan entitlements. Markets differ by plan. Live data requires the matching tier, otherwise you get MARKET_NOT_ENABLED (403).
  2. Check the delayed flag. A delayed field of true in a REST response means that market is currently serving delayed data. Always validate it in production.
  3. Implement heartbeat and reconnect. Long-lived connections need reconnect logic with backoff — see the live quotes over WebSocket guide.
  4. Separate handshake cost from push cost. Handshakes consume quota, so do not "refresh" a connection by reconnecting frequently.
  5. Handle 429 correctly. DAILY_LIMIT_EXCEEDED, MONTHLY_LIMIT_EXCEEDED, and RATE_LIMIT_EXCEEDED all return 429 and need distinct handling.
  6. Estimate with a tool. The quota calculator lets you model quota needs at different symbol scales.

Confirming the migration worked

After switching, validate with three signals:

  • Quota consumption flattens out. Under handshake billing, daily quota use should track connection count — not market volatility.
  • End-to-end latency drops. Compare the tick's at field (an RFC3339 timestamp) against your local receive time.
  • 429s disappear. If DAILY_LIMIT_EXCEEDED still fires after migration, a residual REST poller is still running somewhere.

FAQ

Do WebSocket pushes count against my REST quota?

Not per message. The connection handshake counts toward daily quota and RPM; ticks pushed after the handshake are not metered. That is precisely why streaming cost stays flat.

Why does one-second polling of 500 symbols exceed the quota?

Because the batch limit is 50 codes per call, so a full cycle needs 10 requests. Ten requests per second × 86,400 seconds is 864,000 requests/day — about 25.9 million per month, well past the Pro plan's 5,000,000 monthly allowance.

Can I use REST and WebSocket together?

Yes, and you should. Use WebSocket for live pushes and REST for historical backfill and low-frequency queries. That is TIQEX's documented recommendation.

How do costs change as I add symbols?

With streaming, barely at all — handshake billing is independent of symbol count. With REST polling, growth is linear: double the symbols, double the cost.

How do I confirm I am getting live data rather than delayed data?

Check the delayed field in REST responses and your plan's market entitlements. On Growth, crypto and metals are live while US stocks, A-shares, and Hong Kong are delayed; Pro covers all markets live.

Next steps

Work out your real request volume first, then choose a billing model. The free tier includes delayed crypto, US stocks, and metals with no credit card required — enough to validate your integration end to end. The Python market data guide gets your first request running in about ten minutes.

Check live versus delayed entitlements by market: crypto, US stocks, and forex.