APIs don’t usually fail because the endpoint is down. They fail because your client gets greedy.
You add one more dashboard, one more cron job, one more retry loop… and suddenly you hit the wall. Rate limits. Requests rejected. Data gaps. Angry alerts.
The fix isn’t “send fewer requests.” That’s vague and usually unrealistic.
A rate-limit safe client is built like a budgeting app. It reads the usage headers, predicts remaining capacity, and makes decisions before the API starts rejecting calls. And it still works even when headers are missing.
Here’s the implementation playbook.
What “rate limit headers” actually tell you
In CoinAPI’s Market Data REST API, authenticated endpoints may include usage headers. Prioritize these when present:
X-Credits-Used— credits consumed in the rolling 24 hoursX-Credits-Remaining— credits remaining in the rolling 24 hoursX-RateLimit-Request-Cost— the cost of this response (when provided)
Legacy/alternate names (may still appear on some endpoints):
X-RateLimit-RemainingX-RateLimit-Limit
Concurrency (when present; may vary by product/plan):
X-ConcurrencyLimit-LimitX-ConcurrencyLimit-Remaining
Two details matter a lot:
- The request limit is a rolling 24-hour window, not “midnight reset.”
- Some headers (like Remaining/Used) may not be appended to every response.
That “may not always be appended” line is why you must design for missing headers from day one.
Step 1: Capture headers on every response
Treat headers like telemetry. Every call should capture:
- timestamp (client time)
- endpoint name (or route pattern)
- HTTP status
- the full set of credits/rate limit/concurrency headers (when present)
Store them in your logging/metrics pipeline. This is not “nice to have.” It’s how you debug throttling issues later.
What you want is a state object per API key like:
remaininglimitusedreset_timelast_seen_atrequest_costquota_remaining(if present)concurrency_remaining(if present)
Step 2: Convert headers into a budget you can spend
A clean mental model:
Budget = Remaining capacity over time
Your job is to not spend it faster than it refills.
With a rolling window, you can’t compute “refill per second” perfectly unless the API gives more info. But you can do something practical:
Practical budget strategy (works well in production)
- If you have
X-Credits-Remainingand a reset timestamp (when provided):- compute time until reset:
T = reset_time - now - compute safe average rate:
avg = remaining / T - apply a safety margin (e.g., use 80% of
avg)
- compute time until reset:
- If you do not have a reset time, but you do have limit+used via legacy headers:
- remaining ≈
limit - used - use a conservative fixed pacing (see fallback section)
- remaining ≈
- If you have neither:
- enter “blind mode” and use your own throttling rules + exponential backoff on 429/403.
This gives you a predictable pacing number even with partial info.
Step 3: Account for request “cost,” not just count
This is where teams get burned.
CoinAPI defines “request” as amount of data you ask for, not always “number of HTTP calls.”
If an endpoint supports a limit parameter, then every 100 output items counts as 1 request.
Meaning:
- one HTTP call returning 1000 items could cost ~10 “requests”
- your dashboard might look cheap in call-count but expensive in quota
So your budgeting math should use:
effective_spend = request_cost
from X-RateLimit-Request-Cost
If cost header isn’t present, assume cost = 1 (conservative, but safe).
Step 4: Decide what to do before you hit zero
When remaining capacity drops, your client should degrade gracefully.
A simple rule set that works:
Threshold-based behavior
- Remaining > 20%
Normal operation - 20% ≥ Remaining > 5%
Start pacing:- increase delay between requests
- prioritize critical endpoints
- batch requests where possible
- Remaining ≤ 5%
Survival mode:- stop non-critical background jobs
- serve cached data where acceptable
- avoid large
limitcalls - reduce fan-out (don’t query many symbols per cycle)
Also watch subscription-wide headers:
- If
X-RateLimit-Quota-Remainingis low, you may be fine per key but still break the subscription pool.
That’s why storing both per-key and quota headers matters.
Step 5: Handle missing headers (the part most clients ignore)
CoinAPI notes that some usage headers are not always appended.
So you need a rule:
If headers are missing, keep using the last known state — but decay confidence
Implementation idea:
- Maintain
last_known_remaining,last_known_reset,last_seen_at - If you haven’t seen headers for (say) 5 minutes:
- assume remaining is lower than last known (don’t assume it replenished)
- reduce request rate gradually
- rely more on server responses (429 / Retry-After where applicable)
This avoids the classic mistake: “No headers means unlimited.”
A safe fallback policy
When headers are missing:
- use fixed max requests per second (small)
- use jittered exponential backoff on errors
- slowly ramp up only after a streak of successful responses
This mirrors how good TCP congestion control behaves.
Step 6: Respect concurrency headers too
Rate limits aren’t only “how many per day.” They’re also “how many at once.”
When present, cap in-flight calls based on:
X-ConcurrencyLimit-LimitX-ConcurrencyLimit-Remaining
Practical behavior:
- If
concurrency_remainingis low:- stop launching new requests
- wait for in-flight calls to finish
- reduce parallelism dynamically
This prevents bursts that blow up during peak load.
Step 7: Make retries rate-limit aware (no retry storms)
Retries are where clients accidentally DDoS APIs.
Rules that keep you safe:
- Never retry immediately on 429 / “limit exceeded”
- Use exponential backoff with jitter
- Respect server hints (
Retry-Afterfor WS headers exist; REST often uses reset time) - On repeated throttling, reduce concurrency and request rate globally (not per-call)
Also: don’t retry expensive “high limit” calls in a loop. Switch to smaller pages.
Step 8: Example: “budget loop” logic (language-agnostic)
A simple control loop:
- Send request
- Read headers
- Update internal budget state
- Decide next delay and concurrency cap
- If budget low → degrade features
- If headers missing → fallback pacing
- On 429 → enter backoff mode and lower baseline
This is enough to run stable for months without manual babysitting.
Common mistakes with rate limit headers
- Treating request count as the only cost (ignoring
X-RateLimit-Request-Cost) - Assuming limits reset at midnight (they’re rolling 24h)
- Not handling missing headers (your client becomes blind and overeager)
- Retrying too aggressively (creates spikes and disconnects)
- Using high parallelism without checking concurrency headers
Step 9: Use the Spend Management feature as a safety net
Beyond reading headers in real time, CoinAPI also provides a Spend management feature in the Customer Portal.
This allows you to define usage limits and control whether overage is allowed at the subscription level. If overage is disabled and the subscription reaches its quota, further requests will be rejected until the rolling window resets.
This adds a second safety layer on top of your client-side budgeting logic, helping teams prevent unexpected charges and enforce internal usage policies across environments like production, staging, or public-facing applications.
Final checklist: a rate-limit safe client
✅ Reads and stores usage headers from every response
✅ Budgets based on Credits-Remaining + reset timestamp when available
✅ Uses Request-Cost as the unit of spend
✅ Degrades gracefully near low remaining capacity
✅ Survives missing headers using cached state + conservative pacing
✅ Caps concurrency based on concurrency headers (when present)
✅ Retries with jittered backoff, never in a tight loop
Build It Safe from Day One
A rate-limit safe client is not an optimization. It’s part of your architecture.
Read the headers. Budget your capacity. Handle missing telemetry. Add spend controls at the subscription level.
If you're building on CoinAPI, use both layers:
- client-side budgeting logic
- subscription-level spend management in the Customer Portal
That combination gives you predictable usage, stable integrations, and no surprise rejections.
Start by instrumenting your headers today — before your first 429 forces you to.












