Finance & Data

How do you get live currency exchange rates via API?

Call ReefAPI's currency engine for latest rates, conversion, historical rates, time series and start-to-end fluctuation, all sourced from the European Central Bank. The two behaviours to build around: the response date can differ from your requested date, and an unsupported currency does not fail - it appears in an unavailable list.

Currency & FX engineLive JSON5 steps1,000 free credits

This guide demonstrates the real Currency & FX API engine with a captured response from . The example is only published because the engine passed the SEO snapshot gate.

Use case

FX conversion, finance dashboards, pricing localization and reporting pipelines.

Step by step

Call the live endpoint

  1. 1

    Pick the endpoint by what changes

    latest for today's fixing, historical for a specific date, timeseries for a range of daily rates, fluctuation for start-to-end movement, convert for one amount, currencies for the supported list.

  2. 2

    Send base and symbols explicitly

    base is the currency you hold; symbols narrows the response. Omitting symbols returns all 29, which is 1.5 KB and usually fine.

  3. 3

    Check unavailable before using rates

    An unrecognised code does not fail the call. It appears in unavailable and simply is not in rates. This is the assertion your integration test needs.

  4. 4

    Compare date against requested_date

    Weekend and holiday requests resolve to the previous publication day. Both dates are returned so the substitution is visible rather than silent.

  5. 5

    Carry the attribution

    Every response includes the ECB attribution line. These are reference rates published for information, not transaction or settlement rates, and the string says so.

Code

Copy the request

These snippets use the captured request params for currency/v1/latest.

curl -X POST https://api.reefapi.com/currency/v1/latest \
  -H "x-api-key: $REEF_KEY" \
  -H "content-type: application/json" \
  -d '{"base":"EUR"}'
MCP one-liner
Ask your MCP-connected assistant: call reefapi.currency.latest with {"base":"EUR"}.
Real response

Captured output from ReefAPI

Captured on UTC. The response below is the committed snapshot, including the API envelope and metadata.

Captured request
{
  "method": "POST",
  "url": "https://api.reefapi.com/currency/v1/latest",
  "headers": {
    "x-api-key": "$REEF_KEY",
    "content-type": "application/json"
  },
  "body": {
    "base": "EUR"
  }
}
Captured response
{
  "ok": true,
  "meta": {
    "api": "currency",
    "endpoint": "latest",
    "mode": "live",
    "latency_ms": 1092.4,
    "record_count": 29,
    "bytes": 1547,
    "cache_hit": false,
    "completeness_pct": 100,
    "requests": 1,
    "attribution": "Data source: European Central Bank (ECB) euro foreign-exchange reference rates, freely available at https://www.ecb.europa.eu. Published for information purposes only; not intended for transaction/settlement use.",
    "unavailable": null,
    "charged_credits": 1,
    "version": "1.1.0"
  },
  "data": {
    "base": "EUR",
    "date": "2026-09-23",
    "timestamp": 1790172000,
    "rates": {
      "USD": 1.1411,
      "JPY": 180.2,
      "CZK": 24.383,
      "DKK": 7.4756,
      "GBP": 0.8595,
      "HUF": 364.29,
      "PLN": 4.3765,
      "RON": 5.2785,
      "SEK": 11.272,
      "CHF": 0.939,
      "ISK": 138,
      "NOK": 10.799,
      "TRY": 55.7283,
      "AUD": 1.6146,
      "BRL": 5.8564,
      "CAD": 1.6077,
      "CNY": 7.6538,
      "HKD": 8.9506,
      "IDR": 20305.82,
      "ILS": 3.4417,
      "INR": 109.25,
      "KRW": 1558,
      "MXN": 19.8999,
      "MYR": 4.6557,
      "NZD": 2.0049,
      "PHP": 71.371,
      "SGD": 1.4587,
      "THB": 37.987,
      "ZAR": 18.6537
    },
    "amount": 1,
    "source": "ECB"
  }
}
Manual way

Why this is hard manually

'Live exchange rates' is a phrase that hides a decision. Central-bank reference rates are published once per business day; interbank quotes move by the second and cost real money. Most products that say 'live' actually want the first kind - a stable, citable, non-commercial number for converting a displayed price - and get into trouble when they treat it as the second.

The ECB's rates are the reference standard for that job, and they come with two properties that break naive code. They are published around 16:00 CET on TARGET business days, which means there is no Saturday rate, no Sunday rate and no holiday rate. And they are quoted against the euro, so every non-EUR base is a cross-rate computed from two EUR quotes.

The third trap is coverage. The ECB publishes 29 currencies. Not 150, not crypto, and - since 2022 - not the Russian rouble. A pipeline that assumes any three-letter code will resolve will produce silently incomplete responses rather than errors.

ReefAPI way

Why ReefAPI solves it

When you ask for a date the ECB did not publish, you get the previous publication and both dates in the response. We requested historical for 2026-08-23, a Sunday, and got back date '2026-08-21' - the Friday - with requested_date '2026-08-23' preserved alongside it. Nothing failed and nothing was invented. Key your time series on date if you want distinct rows, on requested_date if you want one row per calendar day, but know which one you picked. fluctuation snaps the same way: start 2026-08-01 came back with start_date 2026-08-03.

An unsupported currency does not raise an error, and this is the single most important thing to check in your integration. We asked latest for symbols 'RUB,EUR,BTC' and got ok:true with rates containing exactly one entry - EUR - and unavailable ['RUB','BTC']. record_count read 1. If you only assert on ok you will ship a converter that silently drops two thirds of its currencies. Assert on unavailable being empty, or on the key you actually need being present in rates.

The 29 currencies the ECB publishes are AUD, BRL, CAD, CHF, CNY, CZK, DKK, GBP, HKD, HUF, IDR, ILS, INR, ISK, JPY, KRW, MXN, MYR, NOK, NZD, PHP, PLN, RON, SEK, SGD, THB, TRY, USD and ZAR, plus EUR as the quote currency. base accepts any of them and the cross-rate is computed for you: base USD returns EUR 0.856971, JPY 159.071043, TRY 48.117319 and so on. The currencies action returns the live set - 30 entries including EUR, each with its full name ('TRY': 'Turkish Lira') and a note about retired codes - so you never have to hard-code ours.

Precision differs between endpoints, deliberately. latest rounds to six decimal places (EUR 0.856971); convert returns the un-rounded rate it used (0.85697146) plus the result, so a converted amount and a rate you multiply yourself agree to the cent. If you are reconciling numbers between two of your own systems, use convert on both sides rather than one of each.

Latency splits sharply by endpoint because only some of them need a fresh fetch. In our runs latest took 1.1 to 2.4 seconds, while historical, convert and fluctuation answered in 19, 53 and 20 milliseconds. Historical data does not change, so it does not need re-fetching.

fluctuation is the endpoint people build by hand and should not. Give it a start and an end and it returns per-currency start_rate, end_rate, change and change_pct in one call: over 2026-08-03 to 2026-08-26 the euro moved -1.1484 percent against the dollar, the yen +1.5263 and the lira +1.2219. Every response also carries an attribution string naming the ECB and noting the rates are for information rather than settlement - that line is part of using the data properly, not decoration.

FAQ

Questions developers ask

Are these real-time rates I can trade on?

No, and no free reference source is. These are European Central Bank reference rates, published once per business day around 16:00 CET. They are the right number for converting a displayed price or reporting a figure; they are not a dealable quote.

I asked for a Sunday and got Friday's date. Is that an error?

No. The ECB does not publish on weekends or TARGET holidays, so we return the most recent publication and keep your input visible: date '2026-08-21' with requested_date '2026-08-23'. Compare the two fields to detect the substitution.

Why did my request for RUB come back ok:true with no RUB?

Because the ECB does not publish it - it has been suspended since 2022. Unsupported codes go into the unavailable array rather than failing the call. We asked for RUB, EUR and BTC and got one rate and unavailable ['RUB','BTC']. Always check that array.

How many currencies are covered?

30 including the euro itself. Call the currencies action for the live list with full names. There is no crypto: BTC is not an ECB reference currency and comes back in unavailable.

Why does convert give a different rate than latest?

Precision, not disagreement. latest rounds to six decimals (0.856971), convert returns the rate it actually used (0.85697146). Use convert on both sides if two of your systems must reconcile to the cent.

Can I get a base other than EUR?

Yes. The ECB quotes everything against the euro and we compute the cross-rate, so base USD, base GBP or base TRY all work. The arithmetic is done from the same daily fixing, so the numbers stay internally consistent.

Do I need to call the API repeatedly through the day?

No, and it would not tell you anything new. There is one publication per business day. Fetch after the daily fixing and cache until the next one; the date field is your cache key.