# Exchange Rates API - Official Central Bank FX Rates (`rel8ble/central-bank-exchange-rates`) Actor

Use this to get official central bank exchange rates (ECB, Poland NBP, Czech CNB, Bank of Canada, Norges Bank), latest or historical. Input: banks, base currency, quote currencies, optional date range. One result = one rate: date, pair, rate, inverse rate, source bank. $0.50 per 1,000 results.

- **URL**: https://apify.com/rel8ble/central-bank-exchange-rates.md
- **Developed by:** [Giovanni Rich](https://apify.com/rel8ble) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Exchange Rates API - Official Central Bank FX Rates (ECB, NBP, CNB, Bank of Canada, Norges Bank)

**Central Bank Exchange Rates** is an exchange rates API and currency rates scraper that pulls **official** daily FX rates straight from five central banks (European Central Bank, National Bank of Poland, Czech National Bank, Bank of Canada and Norges Bank), latest or historical back to 1999, for 150+ currencies and any base currency.

Commercial currency APIs need a key, cap free requests, and don't say where their numbers come from. Accounting, invoicing and VAT often *require* the central bank's reference rate. This actor gives you the bank's own published figure, labels every row with its source and how it was calculated, and runs over plain HTTP with **no API key, no browser and no login**.

### How to use

1. Pick one or more **Central banks** (ECB is the default).
2. Optionally set a **Base currency** (e.g. `USD`) and the **Quote currencies** you need (e.g. `EUR, GBP, JPY`).
3. Leave the dates empty for the **latest published rates**, or set a **Start date** / **End date** for history.
4. Click **Start** and download the rates as JSON, CSV or Excel, or call the actor from the Apify API. A latest-rates run takes about 2 seconds.

### What you get

- **Official rates** from five central banks, each labeled with the bank, its home currency, and the exact figure and convention the bank published (e.g. `1 EUR = x USD`, `100 JPY = x CZK`).
- **Any base currency.** Rates always read "1 base = x quote". Pairs that don't involve the bank's own currency are calculated as cross rates from that bank's fixing on the same day, and marked `cross rate via EUR` (or PLN, CZK, ...) so you always know what you're using.
- **150+ currencies.** NBP table B adds weekly rates for exotic currencies (African, Latin American, Central Asian and more) that most free APIs skip.
- **History**: ECB since 1999, NBP since 2002, CNB since 1991, Bank of Canada since 2017, Norges Bank since 1980. Long ranges are split into chunks automatically (e.g. NBP's 93-day limit).
- **Compare sources.** Pick several banks and you get the same pair from each one on the same date, handy for audits and rate-difference checks.
- **Clean latest mode.** Discontinued series (for example rates a bank stopped publishing years ago) are dropped automatically.

### Use cases

- Accounting and bookkeeping: convert invoices and expenses at the official daily rate
- VAT and tax: EU and Polish/Czech rules often require the ECB, NBP or CNB rate for a specific date
- Finance apps, dashboards and spreadsheets that need a reliable daily FX feed
- E-commerce: reprice catalogs in local currencies every morning
- Research and backtesting with decades of official daily history

### Input example

| Field | Default | Description |
|---|---|---|
| `sources` | `["ecb"]` | `ecb`, `nbp`, `cnb`, `boc`, `norgesbank` |
| `baseCurrency` | each bank's own | e.g. `USD`: every row becomes "1 USD = x quote" |
| `currencies` | all | Quote currencies to keep, e.g. `["EUR","GBP","JPY"]` |
| `startDate` / `endDate` | latest | `YYYY-MM-DD`. Empty start date = latest published rates only |
| `maxResults` | 0 (no limit) | Cap on total rows |

Example:

```json
{
    "sources": ["ecb", "nbp", "cnb", "boc", "norgesbank"],
    "baseCurrency": "USD",
    "currencies": ["EUR", "JPY", "GBP", "PLN", "CAD"],
    "startDate": "2026-09-01",
    "endDate": "2026-09-24"
}
```

### Output example

One row per currency pair, per day, per bank. These are real rows from the run above (24 Sep 2026):

```json
[
    {
        "date": "2026-09-24",
        "base": "USD",
        "quote": "EUR",
        "rate": 0.8797396,
        "inverseRate": 1.1367,
        "pair": "USD/EUR",
        "quoteName": "Euro",
        "source": "ecb",
        "sourceName": "European Central Bank (euro foreign exchange reference rates)",
        "sourceHomeCurrency": "EUR",
        "calculation": "official",
        "sourcePublishedRate": 1.1367,
        "sourceQuoteConvention": "1 EUR = x USD",
        "nbpTable": null,
        "scrapedAt": "2026-09-25T05:17:16.274Z"
    },
    {
        "date": "2026-09-24",
        "base": "USD",
        "quote": "PLN",
        "rate": 3.857,
        "pair": "USD/PLN",
        "source": "nbp",
        "calculation": "official",
        "sourcePublishedRate": 3.857,
        "sourceQuoteConvention": "1 USD = x PLN",
        "nbpTable": "A 186/A/NBP/2026"
    },
    {
        "date": "2026-09-24",
        "base": "USD",
        "quote": "JPY",
        "rate": 158.83146,
        "pair": "USD/JPY",
        "source": "boc",
        "calculation": "cross rate via CAD",
        "sourcePublishedRate": null
    }
]
```

On that day the five banks' USD/JPY came out at 158.48 (NBP), 158.83 (Bank of Canada), 158.85 (CNB), 158.85 (ECB) and 158.86 (Norges Bank). Each bank fixes at a different time of day, so small differences are normal.

The dataset has an **Overview** view: date, pair, rate, inverse, bank, official/cross, and the rate as published.

#### Coverage (real test runs)

| Bank | Currencies (latest run) | Fixing | History from |
|---|---|---|---|
| ECB | 29 vs EUR | Daily, ~16:00 CET | 1999 |
| NBP | 148 vs PLN (32 daily in table A + ~116 weekly in table B) | Daily / Wednesdays | 2002 |
| CNB | 30 vs CZK | Daily, after 14:30 CET | 1991 |
| Bank of Canada | 24 vs CAD | Daily, 16:30 ET | 2017 |
| Norges Bank | 36 vs NOK | Daily, ~16:00 CET | 1980 |

A 5-bank latest run returned 267 rates in 7.6 s; a 5-bank, 18-business-day history run with USD as base returned 445 rates in 3.4 s; an 8-month, 3-bank range for 2 pairs returned 950 rates in 5.8 s. Zero failed requests.

### Pricing

Pay per result: **$0.50 per 1,000 results** (one result = one currency pair on one day from one bank).

- Daily job, 30 currencies from the ECB: ~660 rows/month = **$0.33/month**
- One year of daily history for 10 pairs from one bank: ~2,550 rows = **$1.28**
- Latest rates from all five banks (~270 rows) = **$0.14**

You're never charged for failed requests. Use `currencies` to keep only the pairs you need, and `maxResults` or a maximum cost per run to cap spend. Apify's free plan includes $5 of monthly platform credit.

### Integrations

- **Make, Zapier and n8n**: run it every morning and push the new rates to your accounting tool, CRM or database.
- **Google Sheets**: export rates straight into a sheet and use them in `VLOOKUP`s, or refresh on a schedule.
- **Apify API**: run it and fetch results over REST, or with the official JavaScript and Python clients.
- **Webhooks**: trigger your own pipeline when fresh rates land.
- **Schedules**: a daily Schedule in Apify Console gives you a free-standing daily FX feed.
- **MCP for AI agents**: through the Apify MCP server (https://mcp.apify.com), Claude, ChatGPT, Cursor and other agents can fetch official exchange rates with this actor.

### Limits

- **Business days only.** Central banks don't publish on weekends or bank holidays, so those dates have no rows. For a weekend date, most accounting rules use the previous business day's rate.
- **Fixing times differ.** Each bank fixes at a different time of day, so the same pair differs slightly between banks. Use the bank your rules name.
- **NBP table B is weekly** (Wednesdays), so exotic currencies appear once a week.
- **Cross rates are calculated**, not published. They're exact arithmetic on the same day's official fixing, and every one is marked `cross rate via ...`. If you need a strictly official figure, set the base currency to the bank's own (EUR for ECB, PLN for NBP, and so on).
- **Daily reference rates, not live market quotes.** Don't use them for trading.

### FAQ

**Is this legal? Where does the data come from?**
Every number comes from the central banks' own public data services (ECB Data Portal, NBP Web API, CNB fixing files, Bank of Canada Valet API, Norges Bank SDMX API). They publish these rates for public use. The actor doesn't use any commercial rate provider or personal data. Check each bank's terms if you redistribute the data commercially. This is not legal advice.

**Will it get blocked?**
No blocks in testing: these are official open-data APIs built for machine access. Failed requests are retried with exponential backoff (5 times by default), and a failure at one bank doesn't stop the others. `RUN_SUMMARY` lists any failed request.

**Do I need an API key?**
No.

**Why do I get fewer rows than expected?**
Weekends and holidays have no rates, some currencies are only published by some banks (NBP has the most), and a currency the bank doesn't publish is simply skipped. If your `baseCurrency` isn't published by a bank on a given day, that bank's rows for that day are skipped and the log says so.

**Can I get a rate for one specific date?**
Yes. Set `startDate` and `endDate` to the same day, for example `2026-06-30` for a quarter-end revaluation.

### How it works (for developers)

Each bank has a small adapter that builds its official API URLs (ECB SDMX CSV, NBP JSON tables A/B in 93-day windows, CNB daily/yearly text files, Bank of Canada Valet JSON, Norges Bank SDMX CSV) and parses them into "home currency per 1 unit" observations. Rates are then re-expressed for your base currency using the same bank's same-day figures. Requests run through Crawlee's `HttpCrawler` with retries and a session pool; results are pushed in batches and charged per row.

```bash
npm install
npm test                                  # parser + cross-rate tests
APIFY_LOCAL_STORAGE_DIR=./storage node src/main.js   # input in storage/key_value_stores/default/INPUT.json
```

# Actor input Schema

## `sources` (type: `array`):

Optional. Central banks to query, list of: "ecb" (~30 currencies vs EUR, default), "nbp" (~150 vs PLN), "cnb" (~30 vs CZK), "boc" (~26 vs CAD), "norgesbank" (~40 vs NOK). Several = side-by-side comparison. Default \["ecb"].

## `baseCurrency` (type: `string`):

Optional. 3-letter ISO code so every rate reads "1 base = x quote", e.g. "USD", "EUR", "GBP". Omit to use each bank's own currency. Pairs not involving the bank's currency are cross rates from the same day's fixing (see the "calculation" field).

## `currencies` (type: `array`):

Optional. Only these quote currencies, list of 3-letter ISO codes, e.g. \["EUR", "GBP", "JPY"]. Omit for every currency the bank publishes.

## `startDate` (type: `string`):

Optional. First day of a historical range, format YYYY-MM-DD, e.g. "2025-01-01". Omit to get only the latest published rates. History starts 1999 (ECB), 2002 (NBP), 1991 (CNB), 2017 (Bank of Canada), 1980 (Norges Bank).

## `endDate` (type: `string`):

Optional. Last day of the range, format YYYY-MM-DD. Default today. Banks publish on business days only, so weekends and holidays have no rows.

## `maxResults` (type: `integer`):

Optional. Maximum rate rows in total (one row = one pair, one day, one bank), integer >= 0. Default 0 = no limit.

## `maxRequestRetries` (type: `integer`):

Optional, advanced. Retries per failed or blocked request, integer 0-20; each retry uses a new proxy session. Default 5. Leave unset.

## `proxyConfiguration` (type: `object`):

Optional, advanced. Apify Proxy settings object. Default {"useApifyProxy": false}: no proxy is needed for this public API. Leave unset.

## Actor input object example

```json
{
  "sources": [
    "ecb"
  ],
  "baseCurrency": "USD",
  "currencies": [
    "EUR",
    "GBP",
    "JPY",
    "CAD",
    "CHF"
  ],
  "maxResults": 0,
  "maxRequestRetries": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `rates` (type: `string`):

One row per currency pair per day per central bank.

## `summary` (type: `string`):

Rows, days and date coverage per central bank, plus any failed requests.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "sources": [
        "ecb"
    ],
    "baseCurrency": "USD",
    "currencies": [
        "EUR",
        "GBP",
        "JPY",
        "CAD",
        "CHF"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("rel8ble/central-bank-exchange-rates").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "sources": ["ecb"],
    "baseCurrency": "USD",
    "currencies": [
        "EUR",
        "GBP",
        "JPY",
        "CAD",
        "CHF",
    ],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("rel8ble/central-bank-exchange-rates").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "sources": [
    "ecb"
  ],
  "baseCurrency": "USD",
  "currencies": [
    "EUR",
    "GBP",
    "JPY",
    "CAD",
    "CHF"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call rel8ble/central-bank-exchange-rates --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rel8ble/central-bank-exchange-rates"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/JQpML8S8kIDOSL9I7/builds/mqMsfYYzHtiWramYH/openapi.json
