Exchange-rate API with no key โ€” and rates that hold still for your tests

The two free-tier rate APIs every currency-converter tutorial was built on now refuse keyless requests outright (verified Aug 2026):

$ curl https://data.fixer.io/api/latest
{ "success": false,
  "error": { "code": 101, "type": "missing_access_key", โ€ฆ } }

$ curl https://api.exchangeratesapi.io/v1/latest
{ "error": { "code": "missing_access_key", โ€ฆ } }

So the old tutorial code needs a signup, a key, and a plan for the monthly cap โ€” for an app whose real job is usually "multiply an amount by a number".

๐Ÿ“ก We check exchangerate.host (now key-walled โ€” it answers HTTP 200 with an error envelope, so we probe the body) and keyless Frankfurter, along with 35 other public mock/testing services, every 30 minutes โ€” live status page.

And if the API call lives in your tests, there's a second problem that no key fixes: live rates are non-deterministic. The assertion expect(convert(100, "USD", "EUR")).toBe(85.89) is true today and false tomorrow. A currency-conversion test suite wants rates that hold still.

Below: one curl that hosts a 30-currency dataset (ECB reference rates, a fixed dated snapshot) as your own API. No key, no monthly cap, CORS on, filters/sort/search/pagination, GraphQL, and full CRUD โ€” so you can pin JPY = 150.0 and your expected values stay exact forever.

1. One curl: your own currencies API

We host a ready-made currencies dataset โ€” the euro plus the 29 currencies of the ECB daily reference list: ISO code, name, symbol, minor-unit decimals, rate per 1 EUR, rate per 1 USD (derived), and the snapshot date. Pipe it into the importer:

curl -s https://mockbird.mockbird.workers.dev/data/currencies.db.json \
| curl -s -X POST 'https://mockbird.mockbird.workers.dev/api/projects/import?name=currencies' \
    --data-binary @-

The response has your project id and an adminKey (save it). Your API is live immediately, callable from browser JavaScript:

https://mockbird.mockbird.workers.dev/m/YOUR_ID/currencies

2. Queries

# one currency by ISO code
curl 'https://mockbird.mockbird.workers.dev/m/YOUR_ID/currencies?code=JPY'
# โ†’ [{"code":"JPY","name":"Japanese Yen","symbol":"ยฅ","decimals":0,
#     "rate_eur":185.92,"rate_usd":159.68โ€ฆ,"asOf":"2026-08-28","id":16}]

# free-text search: the six dollars
curl 'https://mockbird.mockbird.workers.dev/m/YOUR_ID/currencies?q=dollar&select=code,name'
# โ†’ AUD, CAD, HKD, NZD, SGD, USD

# zero-decimal currencies โ€” the classic money-formatting edge case
curl 'https://mockbird.mockbird.workers.dev/m/YOUR_ID/currencies?decimals=0&select=code,name'
# โ†’ ISK, JPY, KRW

# range + sort: currencies weaker than 100-per-euro, weakest first
curl 'https://mockbird.mockbird.workers.dev/m/YOUR_ID/currencies?rate_eur_gte=100&sortBy=rate_eur&order=desc&select=code,rate_eur'

Conversion is one line of your own code against the snapshot โ€” no /convert endpoint, no per-call metering:

const rates = Object.fromEntries(list.map(c => [c.code, c.rate_eur]));
const convert = (amount, from, to) => amount * rates[to] / rates[from];

3. Deterministic on purpose

The dataset is a dated snapshot (every record carries asOf), and that's the feature, not the bug, for anything that isn't live commerce:

4. Honest comparison

fixer.io / exchangeratesapi.ioFrankfurterYour Mockbird project
API keyRequired โ€” keyless requests are rejectedNoneNone
RatesLive (frequency depends on plan)Live daily (ECB reference)Fixed dated snapshot (ECB reference)
Deterministic for testsโœ˜ changes constantlyโœ˜ changes dailyโœ” frozen until you change it
Editable / pin a rateโœ˜โœ˜โœ” full CRUD
Failure simulationโœ˜โœ˜โœ” status/delay/chaos/sequence params
Requestsmetered monthly plansfree10,000/day per project
When to use which: if you need current rates in production, use a live API โ€” Frankfurter is free, keyless, open-source, and serves the same ECB reference data daily; for intraday or exotic pairs, the paid tiers of the commercial APIs are the real product. Host your own snapshot when the consumer is a test suite, a demo, a tutorial, or CI โ€” anywhere a rate that moves would break assertions โ€” or when you want to drill the 401/429/timeout paths that live providers can't serve on demand. (Displaying indicative rates to users? Say so, and show the asOf date โ€” don't present a snapshot as live market data.)

5. About this dataset

The dataset contains the European Central Bank's euro foreign exchange reference rates for the date in each record's asOf field (the ECB publishes these freely; they are indicative reference rates, not tradable quotes). rate_usd is derived by cross-rate from the EUR table. Names, symbols, and minor-unit decimals follow ISO 4217 usage. It's your copy โ€” PATCH anything, add currencies the ECB list omits, or re-import a fresh snapshot whenever you like.

Create yours

The one-liner in section 1 is the whole setup โ€” or open the dashboard and paste the dataset into the import panel. Pair it with the countries (each country record carries its currency) or timezones datasets, mock the payment API that consumes these rates, build the rest of a finance dashboard with a mock stock API, or host any CSV as an API.