sifting/io
Developer Tutorials
8 min readSiftingIO Team

Market data API request calculator: estimate calls before choosing a plan

Estimate market data API calls from symbols, polling intervals, shared workers and backfill. Includes worked examples and a tested Python calculator.

Market data API request calculator: estimate calls before choosing a plan

A market data API request estimate starts with your polling schedule, not your user count. Forty symbols fetched individually every five seconds produce 20,736,000 calls in a 30-day month. Fetch those same symbols in one supported snapshot call and the estimate falls to 518,400. That difference can matter more than the first plan you choose.

You can get a SiftingIO API key to test a small watchlist, then use the worksheet below to size it. Start with the market data products you need and compare the result with current plan limits. The examples are capacity estimates, not a guarantee that a particular plan covers every access or display requirement.

Count independent pollers, not registered users#

A service that fetches one watchlist and shares its cached result does not need one upstream request per viewer. However, five independent workers with separate caches may fetch that watchlist five times. A backend proxy alone does not prevent that duplication.

Use these inputs for each distinct request pattern:

InputWhat to count
SymbolsInstruments this poller actually fetches
Symbols per callThe selected endpoint’s supported batch size
Refresh secondsThe interval your application needs
Independent pollersWorkers or jobs that fetch without sharing results
Active seconds per periodThe time the poll loop runs, including accidental overnight polling
Backfill pagesOne-time historical requests, counted separately
Other callsStatus checks, searches, profiles and on-demand chart requests

For a constant schedule, with a fetch at the start of each active window:

calls_per_refresh = ceil(symbols / symbols_per_call) * independent_pollers
refreshes         = ceil(active_seconds / refresh_seconds)
poll_calls        = calls_per_refresh * refreshes
average_calls_sec = calls_per_refresh / refresh_seconds
backfill_calls    = backfill_symbols * ceil(bars_per_symbol / bars_per_page)

Apply the refresh calculation separately to each session when windows differ, then sum them. Add operational headroom for retries and growth. If several markets share an enforced account or team budget, sum their consumption too; separate subscriptions should not be treated as proof of separate runtime counters. Verify the scope and remaining quota in your dashboard and response headers.

Pick the endpoint before picking the plan#

GET /v1/last/quote/{venue}/{symbol} and GET /v1/last/trade/{venue}/{symbol} return one instrument per request. The live-data documentation also describes GET /v1/snapshot/{venue}, whose symbols parameter accepts up to 250 comma-separated symbols in one request.

For a crypto watchlist, a small server-side check looks like this:

curl --silent --show-error --compressed \
  -H "X-API-Key: $SIFTING_KEY" \
  -H "Accept-Encoding: gzip" \
  "https://api.sifting.io/v1/snapshot/crypto?symbols=BTCUSD,ETHUSD,SOLUSD,LINKUSD"

Keep the key out of client code and logs. The snapshot requires gzip support. A returned market snapshot is not a promise that every requested symbol is present and fresh: check the returned symbols and their timestamps against the watchlist.

Batching is useful when the response contains the fields your application needs. One quote endpoint and one snapshot are not interchangeable merely because both count as one request.

Example 1: 40 crypto symbols every five seconds#

Assume one shared poller, 24 hours a day, for 30 days. There are 2,592,000 active seconds and 518,400 refreshes.

DesignCalls per refreshAverage calls/secondCalls/month
One call per symbol40820,736,000
One batched snapshot10.2518,400
Five independent batched workers512,592,000
One batched snapshot every 12 seconds10.0833216,000

The published standard monthly quotas, checked September 30, 2026, are 10,000 for Free, 250,000 for Builder and 5,000,000 for Pro; Ultra lists unlimited REST calls subject to rate limits. Against those figures, the five-second batched example exceeds Builder’s monthly allowance but fits Pro’s numerical allowance. At 12 seconds it is below Builder’s allowance before other calls and headroom.

This is a capacity comparison, not a purchasing recommendation. Historical depth, required markets, streaming limits and permitted use must also fit. Check those requirements alongside the request allowance when comparing plans.

Example 2: a small stock dashboard#

Assume 25 tickers, one batched request per minute, and 21 regular sessions of 390 minutes each. This is an illustrative month; use the actual calendar for production.

WorkCalculationCalls
Price snapshots390 × 218,190
Daily bars, one call per ticker per session25 × 21525
Two status checks per session2 × 2142
Subtotal8,190 + 525 + 428,757
10% planning reserve, rounded upceil(8,757 × 0.10)876
Planning total8,757 + 8769,633

That is below a 10,000-call monthly allowance under these assumptions. Checking market status every minute as well would add another 8,190 calls and break the estimate. Fetch and cache the relevant session calendar, and decide how often status needs refreshing rather than adding an unnoticed call inside every price loop.

The example is for a personal evaluation dashboard. Serving data to your customers requires checking the plan’s display permissions, not just fitting under a request count. Session holidays, half days and extended hours also change the calculation; use the market-hours guide.

Add historical backfill as a separate job#

The documented historical-bar endpoints accept a page limit of up to 2,000 bars. Follow meta.next_cursor rather than assuming a predicted page count is the exact number returned. Empty periods, supported history and session coverage affect the result.

Illustrative one-year windowAssumed one-minute bars per symbolPages at 2,000 bars/page
Non-leap-year, continuously populated 24/7 series525,600263
52 weeks × 5 weekdays × 24 hours374,400188
252 stock sessions × 390 minutes98,28050

These are grids, not promises that every minute has a bar. In particular, 98,280 divided by 2,000 needs 50 pages, not 49.

A one-year, one-minute backfill for 40 continuously populated crypto series therefore budgets 40 × 263 = 10,520 calls. Combined with the five-second batched poller, the first month is 528,920 calls before other work and reserve. You also need a plan whose historical depth covers that window.

The cursor-pagination guide explains how to fetch the pages. Checkpoint completed work so a restart does not fetch the entire history again.

A calculator you can run without an API key#

This Python script calculates consumption only. It deliberately does not issue a plan approval from a hardcoded pricing table.

from math import ceil

def estimate(*, symbols, symbols_per_call, pollers, refresh_seconds,
             active_seconds, backfill_symbols=0, bars_per_symbol=0,
             bars_per_page=2000, other_calls=0, reserve_percent=10):
    values = (symbols, symbols_per_call, pollers, refresh_seconds,
              active_seconds, backfill_symbols, bars_per_symbol,
              bars_per_page, other_calls, reserve_percent)
    if any(type(v) is not int for v in values):
        raise ValueError("Use integer counts, seconds and reserve percent")
    if min(symbols_per_call, refresh_seconds, bars_per_page) <= 0:
        raise ValueError("Batch size, interval and page size must be positive")
    if min(symbols, pollers, active_seconds, backfill_symbols,
           bars_per_symbol, other_calls, reserve_percent) < 0:
        raise ValueError("Counts and reserve cannot be negative")
    calls_per_refresh = ceil(symbols / symbols_per_call) * pollers
    polling = calls_per_refresh * ceil(active_seconds / refresh_seconds)
    backfill = backfill_symbols * ceil(bars_per_symbol / bars_per_page)
    subtotal = polling + backfill + other_calls
    reserve = (subtotal * reserve_percent + 99) // 100
    return {
        "calls_per_refresh": calls_per_refresh,
        "average_calls_per_second": calls_per_refresh / refresh_seconds,
        "polling": polling, "backfill": backfill,
        "subtotal": subtotal, "with_reserve": subtotal + reserve,
    }

result = estimate(
    symbols=40, symbols_per_call=250, pollers=1,
    refresh_seconds=5, active_seconds=30 * 24 * 60 * 60,
    backfill_symbols=40, bars_per_symbol=525_600,
)
assert result["polling"] == 518_400
assert result["backfill"] == 10_520
assert result["subtotal"] == 528_920
assert result["with_reserve"] == 581_812
print(result)

Ten percent is an example reserve, not a measured retry rate. Adjust it from your own traffic and keep a separate spike allowance for unusually busy periods.

Check bursts and streaming separately#

Averages hide synchronized traffic. Forty calls at the same instant are a burst even if the next refresh is five seconds away. Spread requests or use supported batching. A token bucket’s capacity is not necessarily the same number as its sustained refill rate: do not interpret X-RateLimit-Limit as “requests per second.”

Check the actual rate and quota headers on your account. On 429, distinguish a short rate-limit wait from monthly_quota_exceeded, and honor Retry-After. The 429 retry guide covers that handling.

For frequently refreshed prices, compare REST with WebSocket. Streaming can remove the recurring price-poll calls, but it has its own connection and symbol limits, heartbeat requirements and reconnect behavior. Historical bars and other REST work still need a budget. Evaluate those constraints independently rather than putting WS messages into a REST call formula.

Validate the estimate with a small run#

Run the intended architecture with a limited watchlist. Measure upstream calls, duplicate refreshes, error responses, cache misses and reconnects. Compare those observations with the calculator before expanding the symbol list.

Then choose a plan based on the actual request pattern, required history and use case. If the numbers are close to a limit, send us the worksheet so we can help size the setup before it becomes a production problem.

Keep reading

Related posts