Open Exchange Rates Time-Series Burns Your Quota
If you've shipped anything against Open Exchange Rates that touches history, you've probably hit this the hard way. Their documentation is explicit: "Requests to the time-series.json API endpoint count as one request for each day of data returned."
Meaning:
- 7 days of data = 7 requests
- 30 days = 30 requests (3% of your free 1,000/month cap)
- 90 days = 90 requests (9%)
- 365 days = 365 requests (36.5% of your free monthly quota in a single call)
For a charting use case that backfills a year of daily rates across 5 currency pairs, you're at 5 × 365 = 1,825 requests — past the free tier in a single page load.
AllRatesToday prices historical differently: one request per month of data, not per day, so a year of daily rates costs 12 requests instead of 365. Historical data is included on every paid plan, from €4.99/month for 5,000 requests; the free plan covers latest rates only. This article explains the difference, what it costs in practice, and how to design around either.
The quota model
OpenExchangeRates time-series.json
GET /api/time-series.json?app_id=XXX&start=2025-01-01&end=2025-12-31&base=USD&symbols=EUR
# costs: 365 requests against your monthly quota The endpoint returns all 365 daily rates in one HTTP call — but internally it's billed as if you'd made 365 individual historical requests.
AllRatesToday /api/v1/rates with a date range
GET /api/v1/rates?source=USD&target=EUR&from=2025-10-17&to=2026-10-16&group=day
# costs: 12 requests against your monthly quota (one per month of data) One HTTP call, 12 quota units: about 30 times cheaper than per-day billing.
Concrete scenarios
Scenario 1: Dashboard with 5 pairs × 90-day history
- OpenExchangeRates: 5 × 90 = 450 requests per dashboard load. Past the free 1,000/mo cap after two loads.
- AllRatesToday: 5 × 3 = 15 requests per load (90 days is 3 months of data). Cache the result for the day and the same dashboard fits comfortably within the €4.99/month Small plan (5,000 requests/month).
Scenario 2: Tax-year backfill (1 year, 1 pair)
- OpenExchangeRates: 365 requests. One-shot operation consumes 36.5% of the monthly quota.
- AllRatesToday: 12 requests. A small slice of a 5,000-request Small plan.
Scenario 3: Multi-pair backtest (10 pairs × 1 year)
- OpenExchangeRates: 10 × 365 = 3,650 requests. Past the Developer plan's 10,000/month after three runs.
- AllRatesToday: 10 × 12 = 120 requests. Market history goes back one year; for longer backtests use the central-bank archives, which go back decades.
The OpenExchangeRates model effectively turns historical analysis into a line-item purchase: every day of data you want, you pay for.
Why the pricing model exists
Historical data is bandwidth-heavy. An "all currencies for one day" response can be 5-10 KB. Open Exchange Rates' per-day billing reflects internal cost: each day is a separate backend lookup. It's defensible from an infra economics perspective.
It's still a bad developer experience. When your request count doesn't match your HTTP call count, capacity planning gets confusing fast. You have to explain to new team members why one line of code burned 365 requests.
AllRatesToday also bills by volume, but by the month instead of the day: one request per currency pair per month of data. A month of daily rates costs the same as a single lookup, and a year costs 12. The charge is easy to predict before you send the call.
When per-day billing is fine
- You only pull history for specific dates (accounting, invoice reconciliation). Open Exchange Rates'
historical/{date}.jsonis one request per date — the same price as AllRatesToday for single-date queries. - You rarely look at ranges — your app mostly uses current rates.
When range-based pricing matters
- Charting: every dashboard render pulls a range.
- Analytics: rolling 30/90/365-day windows.
- Backtesting: long ranges across many pairs.
- Reporting: monthly/quarterly/yearly recaps.
Any of these become about 30x cheaper in quota terms on AllRatesToday.
AllRatesToday historical endpoint, in detail
curl -X GET "https://allratestoday.com/api/v1/rates?source=USD&target=EUR&from=2025-10-17&to=2026-10-16&group=day" \
-H "Authorization: Bearer YOUR_API_KEY" Response:
[
{ "rate": 0.893176, "source": "USD", "target": "EUR", "time": "2026-10-15T00:00:00+0000" },
{ "rate": 0.892777, "source": "USD", "target": "EUR", "time": "2026-10-14T00:00:00+0000" },
...
{ "rate": 0.854774, "source": "USD", "target": "EUR", "time": "2025-10-17T00:00:00+0000" }
] Rows come newest first, one per day. A from/to range on /api/v1/rates for one target is billed one request per month of data (a 30-day range costs 1). There is also a shortcut endpoint, /api/historical-rates?period=1d|7d|30d|1y (Bearer header only), which bills one request per call except period=1y, billed as 12 (one per month of data). Historical data needs a paid plan.
Migrating a time-series query
Most apps using Open Exchange Rates' time-series.json pass start, end, and a base/symbols combination. The equivalent AllRatesToday call is:
- https://openexchangerates.org/api/time-series.json?start=2025-01-01&end=2025-12-31&base=USD&symbols=EUR&app_id=XXX
+ https://allratestoday.com/api/v1/rates?source=USD&target=EUR&from=2025-01-01&to=2025-12-31&group=day
(Authorization: Bearer YOUR_API_KEY) Response shape is different (array of {time, rate} vs keyed object), but the transformation is straightforward.
What if you still need per-day granularity?
Both APIs support single-date historical lookups at one-request-per-date:
- OpenExchangeRates:
GET /api/historical/2025-06-15.json - AllRatesToday:
GET /api/v1/rates?source=USD&target=EUR&from=2025-06-15&to=2025-06-15
If your app cares about a handful of specific dates rather than ranges, either API is fine.
Stop burning quota on history
Historical data from €4.99/month, billed per month of data instead of per day, 60-second real-time updates. Start with 300 free requests, no credit card.
Get your free API key