# Exchange Rates Scraper: ECB Daily FX & History (`recordsdata/exchange-rates-scraper`) Actor

Scrape official ECB exchange rates: latest daily reference rates and daily history back to 1999 for 30+ currencies, from any base, with inverse rates on every row. Multi-base batches and a currency directory. Export CSV, Excel, JSON, XML. No login or API key. 29 currencies per USD-base request.

- **URL**: https://apify.com/recordsdata/exchange-rates-scraper.md
- **Developed by:** [RecordsData](https://apify.com/recordsdata) (community)
- **Categories:** Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

<p align="center">
  <img src="https://api.apify.com/v2/key-value-stores/AAm3a1h3Z9nYfrvh9/records/banner?v=3" alt="RecordsData" width="100%" />
</p>

## 💱 Exchange Rates Scraper: 165 Currencies, History, OHLC

> **Exchange Rates Scraper exports exchange rates for 165 currencies, blended from 104 central banks, as clean rows: latest rates, daily history back to 1999 (older for some pairs), weekly to yearly open-high-low-close, amount conversion, change versus the previous fixing, and a monitor mode that skips unchanged rates for free. No login, no API key. Export CSV, Excel, JSON or XML.**

Exchange Rates Scraper turns the open Frankfurter v2 service into one row per base currency, target currency and date, with a `source` field saying which central bank feed produced it. Besides the 165 current currencies it covers 58 retired ones (DEM, FRF, ATS, GRD, CYP...) for history. Built for finance teams, BI engineers and researchers who need an auditable FX table.

### 💱 What does Exchange Rates Scraper do?

- **Latest rates for 165 currencies**, with the change against the previous fixing.
- **ECB only, on request**: set `providers` to `["ECB"]` for the 45 ECB currencies on business days. Keys such as BOE, BOJ, BANXICO or BCB work the same way.
- **History since 1999**: daily rates for any range, earlier for some pairs (USD/JPY from 1969). `historyFrom` accepts `30d`, `12w`, `6m`, `5y`, `ytd`.
- **OHLC intervals**: `daily`, `weekly`, `monthly`, `quarterly` or `yearly` rows with open, high, low, close, average and range percent.
- **Amount conversion**: set `amount` and every rate row gets `converted`.
- **Monitor mode**: `onlyChanged` returns only new or changed rates; unchanged rates are not charged.
- **Directories** of currencies and sources, and multi-base batches.

### 📊 What data can you extract from the exchange rates?

| Field | Description |
|---|---|
| `recordType` | `rate`, `currency` or `provider` |
| `rateType` | `latest` or `historical` (rate rows) |
| `interval` | `daily`, `weekly`, `monthly`, `quarterly`, `yearly` (history rows) |
| `changeType` | `new` or `changed` (monitor mode) |
| `date` | Fixing date, YYYY-MM-DD (last fixing of the period on aggregated rows) |
| `period`, `periodStart`, `periodEnd` | Aggregated rows: `2025-W03`, `2025-01`, `2025-Q1` or `2025`, and bounds |
| `base`, `baseName` | Base currency code and full name |
| `currency`, `currencyName`, `pair` | Target currency code, full name and pair |
| `rate` | Units of target per 1 unit of base (closing rate on aggregated rows) |
| `inverseRate` | 1 / rate, 10 significant digits |
| `amount`, `converted` | Only when the `amount` input is set |
| `previousRate`, `previousDate`, `change`, `changePct` | Versus the previous fixing, or previous period close |
| `open`, `high`, `low`, `average`, `rangePct`, `days` | Aggregated rows: period statistics and days with a rate |
| `source` | `blended`, or the source keys you chose (for example `ECB`) |
| `providerCount`, `providerRates` | Latest rows with `includeProviderRates`: each source's rate and date |
| `name`, `isoNumeric`, `symbol`, `isActive`, `firstDate`, `lastDate` | Currency directory rows |
| `provider`, `country`, `rateKind`, `frequency`, `publishCadence`, `currencyCount`, `currencies`, `dataUrl` | Source directory rows |
| `scrapedAt` | ISO timestamp of the run |
| `error` | Only on a failed lookup row (never billed) |

Fields that do not apply to a row are omitted.

### 📄 Exchange rates sample output

A latest-rate row from a cloud run (2026-10-04):

```json
{
  "recordType": "rate",
  "rateType": "latest",
  "date": "2026-10-02",
  "base": "USD",
  "currency": "BRL",
  "rate": 5.2214,
  "inverseRate": 0.19151952,
  "scrapedAt": "2026-10-04T04:19:31.176Z"
}
```

### 💰 How much does it cost to get exchange rates?

Pay per event: you pay only for rows saved.

| Event | Price (Free plan) | Lower on paid Apify plans |
|---|---|---|
| Rate row (`rate-record`) | $0.012 per row ($12 per 1,000) | down to $0.010 per row |
| Currency or source directory row (`currency-record`) | $0.010 per row | $0.010 per row |

An aggregated row (weekly, monthly, quarterly, yearly) costs the same as a daily row, so aggregating makes a series much cheaper. Worked examples on the Free plan:

- USD latest rates for 10 currencies: 10 x $0.012 = $0.12.
- One pair, one year of daily rows from the blended feed (365 days): about $4.38. The same year as weekly rows (52): about $0.62.
- USD latest for all currencies plus both directories with retired currencies: 164 rates and 327 directory rows, about $5.24 (cloud run: 164 `rate-record`, 327 `currency-record`).

On the top paid tier the rate row is $0.010, so the year of daily rows is $3.65 and the weekly one $0.52. Error rows are free, and a run that delivers no rates fails with a clear message and charges nothing. Your `maxTotalChargeUsd` limit is respected.

### 🚀 How to scrape exchange rates in 3 steps

1. Open the actor on Apify and click **Try for free**.
2. Enter your base currencies (for example `USD`), optionally target currencies, an `amount`, and a history range with an interval.
3. Click **Start**, then download the dataset as CSV, Excel, JSON or XML.

### ⚙️ Exchange Rates Scraper input

| Field | Default | What it does |
|---|---|---|
| `baseCurrencies` | `["USD"]` | Bases to quote from (3-letter ISO codes, retired ones work for history) |
| `symbols` | `[]` | Keep only these target currencies; empty means all |
| `providers` | `[]` | Source keys instead of the blend, for example `["ECB"]`; empty means blended |
| `includeLatest` | `true` | Add the latest rates |
| `amount` | empty | Adds `amount` and `converted` to rate rows |
| `includeChange` | `true` | Adds previous rate, change and change percent |
| `includeProviderRates` | `false` | Adds each source's rate to latest rows |
| `historyFrom` | empty | Start: YYYY-MM-DD or `30d`, `12w`, `6m`, `5y`, `last 5 years`, `ytd` |
| `historyTo` | empty | End: YYYY-MM-DD, `yesterday` or `today`; empty means today |
| `interval` | `daily` | `daily`, `weekly`, `monthly`, `quarterly` or `yearly` |
| `includeCurrencyList` | `false` | Add one row per currency |
| `includeRetiredCurrencies` | `false` | Also list retired currencies in the directory |
| `includeProviderList` | `false` | Add one row per rate source |
| `onlyChanged` | `false` | Return only new or changed rates |
| `stateKey` | empty | Name of the monitor memory; empty derives it from your inputs |
| `resetSeen` | `false` | Forget previous results before the run |
| `maxItems` | `100` | Maximum rows returned (free users: 10) |

Example input:

```json
{
  "baseCurrencies": ["USD", "EUR"],
  "symbols": ["MXN", "JPY", "GBP"],
  "amount": 100,
  "historyFrom": "5y",
  "interval": "monthly",
  "maxItems": 5000
}
```

### 📦 Output behavior

A wrong source key or currency code fails the run before any request, with the list of valid values, and nothing is charged. A genuinely empty result finishes as SUCCEEDED with no charge.

### ⚖️ Exchange rates API alternatives compared

Competitor Store pages read on 2026-10-09. A feature marked "not stated" is not shown in that listing.

| | RecordsData | chorelet/currency-rates-scraper | parseforge/ecb-fx-rates-scraper |
|---|---|---|---|
| Price per row | $0.012 Free, $0.010 top tier | $0.0002 | $0.0075 |
| Currencies | 165 blended, 45 ECB | 165 global (today only), 29 ECB | ECB; count not stated |
| History | 1999, older for some, all 165 | ECB since 1999 | Since 1999 |
| Weekly to yearly OHLC | Yes, plus quarterly | Weekly, monthly, yearly | Not stated in their listing |
| Amount and change % | Yes, both | Yes, both | Not stated in their listing |
| Only-changed monitor, free skips | Yes | Not stated in their listing | Not stated in their listing |
| Retired currencies | 58, any range, in directory | Some, within their lifetime | Not stated in their listing |
| Source directory, per-source rates | Yes | Not stated in their listing | Not stated in their listing |

We cost 50 to 60 times more per row than chorelet, and chrisp1211/currency-exchange-scraper-max ($0.0004, daily rows only) and benthepythondev/ecb-exchange-rates-scraper ($0.0015, ECB only) are cheaper too. If you only need rates and weekly or monthly OHLC, a cheaper listing may be enough. Pick this one for 165-currency history, monitoring, source transparency and retired currencies. Rates are daily fixings: no intraday or bid/ask, and no `sourceUpdatedAt` field.

### 🧰 Exchange rates use cases

Month-end close, FX dimension tables in a warehouse, price localization with alerts, economic research panels including pre-euro currencies, and treasury comparison of central bank rates.

### 🔌 Exchange Rates Scraper API, schedules and integrations

Run it from the API with the Apify client (`client.actor('RecordsData/exchange-rates-scraper').call({ baseCurrencies: ['USD'], historyFrom: '12w', interval: 'weekly' })`). Schedule it daily with `onlyChanged` on, or connect it to Zapier, Make, n8n or Airbyte.

### ⚖️ Is it legal to scrape exchange rates?

Central bank reference rates are public data, and Frankfurter is an open service that republishes them without a key. The actor collects no personal data. For compliance or audit use, cite the central bank in the `source` field and read the publisher's own terms on reuse.

### 🤖 Exchange Rates Scraper MCP server for AI agents

AI agents can call this Actor through the Apify MCP server at `https://mcp.apify.com?tools=RecordsData/exchange-rates-scraper`. The URL loads only this Actor as a tool.

Add this to your MCP client configuration (Claude Desktop, Cursor, VS Code):

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=RecordsData/exchange-rates-scraper"
    }
  }
}
```

Example agent prompt:

> Use the Exchange Rates Scraper to get weekly USD to MXN, JPY and GBP rates for the last 12 months, with high and low per week, convert 1,000 USD at each closing rate, and tell me the week with the biggest range.

An agent should choose this tool when it needs official, auditable exchange rates (latest or history since 1999) with conversion, change or monitoring, rather than a live market quote.

### ❓ Frequently asked questions

#### Where do the Exchange Rates Scraper rates come from?

The Exchange Rates Scraper reads the open Frankfurter v2 service. By default it returns a blend of rates published by 104 central banks and institutions, and every row says `source: blended`. With `providers` set to `["ECB"]` you get European Central Bank reference rates only. These are official fixings published once per business day, not live market quotes.

#### Which currencies does the exchange rates scraper cover?

It covers 165 current currencies in the blended feed, including USD, EUR, GBP, JPY, MXN, BRL and CNY, and 45 with the ECB alone. It also covers 58 retired currencies such as DEM, FRF and GRD for history. Turn on `includeCurrencyList` for one row per currency with name, ISO number and symbol, and `includeRetiredCurrencies` to list the retired ones.

#### How far back does the exchange rate history go?

The Exchange Rates Scraper returns daily history back to 1999 for most pairs, and earlier for some, for example USD/JPY from 1969. Set `historyFrom` and `historyTo` to choose the range. With the ECB source, weekends and holidays have no fixing, so only business days come back.

#### Why did the Exchange Rates Scraper return 0 results?

Usually the date range holds no fixing, or `onlyChanged` is on and nothing moved since the last run, which finishes as SUCCEEDED and charges nothing. A wrong currency code, source key or date format makes the run fail before any request, with a message that lists the valid values, and nothing is charged.

#### Does the Exchange Rates Scraper work with a base other than the euro?

Yes. Any of the 165 currencies can be the base, for example USD, GBP or MXN, and so can retired currencies for history. You can list several base currencies in `baseCurrencies` and get all of them in a single run, with `inverseRate` computed on every row, so no extra runs are needed.

#### Why is the Exchange Rates Scraper free plan limited to 10 rows?

Free Apify users get a 10-row preview so they can check the output before paying. Paid Apify plans run up to 1,000,000 rows per run, and you can still cap spending with `maxTotalChargeUsd` and the `maxItems` row limit, which defaults to 100, so tests stay cheap.

#### Am I charged for errors when I scrape exchange rates?

No. Error rows and failed runs are not billed, and a run that delivers no rates fails with a clear message and charges nothing. You pay only for rows saved to the dataset, and `maxTotalChargeUsd` stops the run cleanly at your limit.

#### Are these exchange rates live market rates?

No. The scraper returns official daily fixings published by central banks, one per day. They suit accounting, reporting, invoicing and analysis, but they are not live market quotes, there is no bid/ask or intraday data, and they are not intended for trading. Each row shows the fixing date and the `source`.

#### How do weekly, monthly and yearly OHLC rows work?

Set `interval` to `weekly`, `monthly`, `quarterly` or `yearly` together with a history range. You get one row per pair and period with `open`, `high`, `low`, `rate` (the close), `average`, `rangePct`, `days` and the change against the previous close. An aggregated row costs the same as a daily row.

#### Can I use relative date ranges like the last 30 days?

Yes. `historyFrom` accepts `30d`, `12w`, `6m`, `5y`, `last 30 days`, `last 5 years` or `ytd`, and `historyTo` accepts `yesterday` or `today`. A scheduled run with `30d` always covers the latest 30 days, and the monitor memory uses the text you typed, so it stays stable from day to day.

#### How does monitor mode work?

Turn on `onlyChanged` and schedule the Exchange Rates Scraper. A latest rate is `new` the first time and `changed` when its date or value moves; history rows are tracked by pair, date and interval. Unchanged rows are skipped before the row cap and before charging, so a quiet day costs nothing. Use `stateKey` to keep separate monitors and `resetSeen` to start over.

#### How do I convert an amount with the Exchange Rates Scraper?

Set the `amount` input, for example 100 with base USD. Every rate row then carries `amount` and `converted` (amount x rate), so you get the value of 100 USD in each currency or on each historical date.

#### What are the rate limits for old history?

The data service allows 10 requests per minute for ranges starting before 2021. The scraper spaces those requests about 6.5 seconds apart and honours the wait the service asks for, so fifteen years of all currencies finished in about 150 seconds in our tests. You are billed per row only, not for waiting time.

### 🛟 Support

Found a bug or missing field? Open the **Issues** tab on this actor's page or write to contact.punkrecordsdata@gmail.com. Custom solutions are available on request.

Last updated: 2026-10-11

# Actor input Schema

## `baseCurrencies` (type: `array`):

Base currencies to quote from, as 3-letter ISO codes (USD, EUR, GBP, MXN, BRL). The scraper returns one set of exchange rates per base, with the inverse rate on every row. Retired currencies such as DEM, FRF or ATS also work for history.

## `symbols` (type: `array`):

Optional target currencies to keep, as 3-letter ISO codes (EUR, MXN, JPY). Leave empty to get all 165 currencies of the blended feed (45 with the ECB source).

## `providers` (type: `array`):

Central bank or institution keys to use instead of the blended feed, for example ECB (euro reference rates), BOE, BOJ, BANXICO, BCB, BCCH, BANREP, TCMB. Leave empty for the default: rates blended across 100+ central banks, 165 currencies. Turn on the source directory to list every key.

## `includeLatest` (type: `boolean`):

Fetch the latest rate of every pair, one row per base-currency pair, with its change against the previous fixing. Turn off if you only need history or the directories.

## `amount` (type: `number`):

When set, every rate row gets the amount and the converted value (amount x rate) in the target currency, for example 100 with base USD gives the value of 100 USD in each currency.

## `includeChange` (type: `boolean`):

Adds previousRate, previousDate, change and changePct to each row. Daily rows compare with the previous fixing of the pair, weekly, monthly, quarterly and yearly rows with the previous period close.

## `includeProviderRates` (type: `boolean`):

Latest rows get providerCount and providerRates: the rate each central bank published for that pair, with its date, so you can see how much the sources agree. Larger rows.

## `historyFrom` (type: `string`):

Start of the history range: a date like 2025-01-31 or a relative range like 30d, 12w, 6m, 5y, "last 30 days", "last 5 years" or ytd. History starts in 1999 for most pairs and earlier for some (USD/JPY from 1969). Leave empty to skip history.

## `historyTo` (type: `string`):

End of the history range: a date like 2025-12-31, or yesterday or today. Leave empty to use today.

## `interval` (type: `string`):

Daily returns one row per pair and day. Weekly, monthly, quarterly and yearly return one row per pair and period with the closing rate, open, high, low, average, tradingDays and the change against the previous period.

## `includeCurrencyList` (type: `boolean`):

Add one row per supported currency with its full name, ISO number, symbol, first and last rate date.

## `includeRetiredCurrencies` (type: `boolean`):

Also list currencies that no longer exist (DEM, FRF, ATS, GRD, CYP and about 50 more). Their history stays available as base or target.

## `includeProviderList` (type: `boolean`):

Add one row per rate source (central bank): key, name, country, rate type, pivot currency, frequency, first and last date, and the currencies it publishes.

## `onlyChanged` (type: `boolean`):

Return a rate only when it is new or its value changed since the last run with the same inputs. Unchanged rates are skipped and not charged. Each row carries changeType new or changed. Ideal for a daily schedule.

## `stateKey` (type: `string`):

Name for the memory of previous results. Leave empty to derive it from your inputs. Use the same name to share memory between runs, or a different name to keep separate monitors.

## `resetSeen` (type: `boolean`):

Clear the memory for this monitor before the run, so every rate counts as new again.

## `maxItems` (type: `integer`):

Maximum number of rows to return (rate, currency and source rows count together). Free users are limited to 10 rows. Paid users: up to 1,000,000.

## Actor input object example

```json
{
  "baseCurrencies": [
    "USD",
    "EUR"
  ],
  "symbols": [],
  "providers": [],
  "includeLatest": true,
  "amount": 100,
  "includeChange": true,
  "includeProviderRates": false,
  "historyFrom": "",
  "historyTo": "",
  "interval": "daily",
  "includeCurrencyList": false,
  "includeRetiredCurrencies": false,
  "includeProviderList": false,
  "onlyChanged": false,
  "stateKey": "",
  "resetSeen": false,
  "maxItems": 10
}
```

# Actor output Schema

## `overview` (type: `string`):

Key fields per row

## `fullData` (type: `string`):

Complete dataset with all fields

# 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 = {
    "baseCurrencies": [
        "USD",
        "EUR"
    ],
    "symbols": [],
    "providers": [],
    "amount": 100,
    "historyFrom": "",
    "historyTo": "",
    "stateKey": "",
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("recordsdata/exchange-rates-scraper").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 = {
    "baseCurrencies": [
        "USD",
        "EUR",
    ],
    "symbols": [],
    "providers": [],
    "amount": 100,
    "historyFrom": "",
    "historyTo": "",
    "stateKey": "",
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("recordsdata/exchange-rates-scraper").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 '{
  "baseCurrencies": [
    "USD",
    "EUR"
  ],
  "symbols": [],
  "providers": [],
  "amount": 100,
  "historyFrom": "",
  "historyTo": "",
  "stateKey": "",
  "maxItems": 10
}' |
apify call recordsdata/exchange-rates-scraper --silent --output-dataset

```

## MCP server setup

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

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/dVJNow6rGidD3mT6b/builds/576H4i079NlwZgAz6/openapi.json
