biquote
Home / Docs / Guides

My free API serves 13.8 million requests a day. One guy makes 9 million of them.

People keep asking what it costs to run a free market data API. I kept answering vaguely, so last week I counted a whole day.

requests   13,792,750
egress          13.24 GB
unique IPs       2,468

160 requests a second, all day. I assumed something was broken. Then I split it by IP:

9,188,005   69.62.119.161      <- 67% of everything
  861,289   34.96.41.250
  769,412   34.96.41.75
  ...
    2,468 unique IPs

One address is two thirds of my traffic. Top three, 78%. Top twelve, 95%. Everyone else — the ~2,450 people I actually pictured when I built this — split the remainder.

So what's he doing?

1,132,840   GET /api/{symbol}/ohlc?interval=1m&limit=1
   93,198   GET /api/latest?symbols=...   (20 at a time)

Fetching the latest one-minute candle, one symbol per request, in a loop, across every instrument I carry.

Look at the second line though. He already uses the batch endpoint. It's in his code, twenty symbols per call, exactly how I'd want it done. He just also has this other loop, and the other loop is 92% of his volume.

That's not carelessness. He wrote the single-symbol version first, it worked, and nothing ever pushed him to revisit it. Nothing got slower. No bill showed up. From where he's sitting the two are identical, because I made them identical.

The number that actually bothers me

/api/{symbol}/ohlc   1,234,795
/api/{symbol}          465,157
/api/latest            150,890
/hubs/tick              42,964

/hubs/tick is the WebSocket. Ticks get pushed to you, it doesn't count against the rate limit, and it's far cheaper for me than answering the same GET four hundred times a minute.

All day, it completed 744 upgrades.

744 against 13.8 million polls. The option that's better for both of us is the one nobody takes. I went and reread my own docs afterwards and the WebSocket section is accurate and totally unconvincing — it explains how SignalR works instead of saying "this one is free and polling isn't."

What it costs

I set this up expecting to end with a tidy monthly invoice. There isn't one. The API runs on a machine I already have that does other things too, so the real question is how much of it 13.8M requests actually eat:

biquote container   141.5 MiB RAM    ~1/3 of a core
redis               116.9 MB         7,130 keys

That's everything. Symbols, candles, socket fan-out, economic calendar. 140 megabytes.

What's genuinely not free: about 400 GB of egress a month, which on a metered cloud VM would be ~$36 and would scale with exactly the polling above. And 13 GB of nginx logs sitting on disk, which I never once thought about before being free made it a real number.

The rate limit isn't doing what I thought

15,000 requests per minute per IP. I picked it to be so far past reasonable that nobody would ever hit it.

Our friend averages 6,400 a minute. Well under. Completely legitimate. He's doing nothing wrong.

So it's not a fairness mechanism, it's just a DoS brake. "Free for everyone, generous per-IP limit" turns out to mean "mostly free for whoever wants it most." My capacity planning is really just tracking one stranger's cron job.

I'm not lowering it. The limit isn't shaping the traffic — the incentives are, and nobody has any reason to poll less.

What I'm changing is smaller: the docs lead with the socket now instead of burying it under six REST sections, the 429 body names the batch endpoint and the socket instead of just saying slow down, and every symbol page shows the socket snippet right next to the curl one. On a free API you don't get to choose how people use it. You only get to choose which snippet is easiest to copy, and that turns out to matter more than the rate limiter.

If it's useful

Real-time forex, metals, crypto and index CFDs over REST and a socket, plus candles and an economic calendar. No key, no signup.

curl "https://biquote.io/api/latest?symbols=EURUSD&symbols=XAUUSD"

npm install biquote / pip install biquote, source at github.com/halitcanbaba/biquote-sdk. It's broker CFD pricing, not exchange data — no last price, no volume, no order book. Fine for live FX and metals, wrong for backtesting equities over decades.

Read the API docs →