# Exchange Rates Scraper: ECB Official Rates (`glitchbound/fx-scraper`) Actor

Official European Central Bank reference rates for 30 currencies: latest, any historical date, or a full time series. The figure European accounting and tax rules cite. Weekends and holidays are flagged rather than silently filled. No key, no quota.

- **URL**: https://apify.com/glitchbound/fx-scraper.md
- **Developed by:** [Daniel Meshulam](https://apify.com/glitchbound) (community)
- **Categories:** Automation, E-commerce, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 rates

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Exchange Rates Scraper: ECB Official Rates

Official European Central Bank reference rates. Latest, any historical date, or a
full time series.

```json
{ "mode": "latest", "baseCurrency": "USD", "targetCurrencies": ["EUR", "ILS", "GBP"] }
```

No key. No quota. No attribution string buried in the response.

### Why ECB rates and not a "free FX API"

There are a dozen free FX services and they mostly aggregate from unnamed
sources. ECB reference rates are published by a central bank, once per working
day around 16:00 CET, and they are **the figure European accounting and tax
rules actually cite**. If a number has to survive an audit, provenance is the
feature.

Every row says so: `source: "ECB reference rate"`.

### The weekend problem, handled properly

The ECB publishes on working days only. Ask most FX APIs for a Sunday and they
hand back Friday's number stamped with Sunday's date. That is a quiet
fabrication, and it compounds when you build a time series out of it.

Here, `date` is always **the date the ECB actually published**. When that differs
from what you asked for, the row says so:

```json
{
  "date": "2026-01-16",
  "requestedDate": "2026-01-18",
  "currency": "ILS",
  "rate": 3.61,
  "note": "no rate published on the requested date (weekend or holiday); this is the previous working day"
}
```

You can still use it. You just are not misled about what it is.

### Three modes

```json
{ "mode": "latest", "baseCurrency": "EUR" }
```

```json
{ "mode": "range", "startDate": "2026-01-01", "endDate": "2026-06-30",
  "baseCurrency": "USD", "targetCurrencies": ["ILS"] }
```

```json
{ "mode": "dates", "specificDates": ["2026-01-15", "2026-04-01"],
  "baseCurrency": "USD" }
```

Leave `targetCurrencies` empty for all 30 currencies the ECB publishes.

### Use cases

- **Invoicing and accounting**: the rate on the invoice date, from a citable source
- **Multi-currency reporting**: one row per date and currency, ready to join
- **Backtesting**: a full daily series in one run
- **E-commerce pricing**: scheduled daily refresh of your conversion table
- **Expense and travel tools**: historical rate on the transaction date

### Pricing

Charged per rate returned. A date the ECB never published for, or a currency it
does not track, produces an `error` row and costs **$0.00**.

### Three modes, and which one you want

Backed by the **ECB's official daily reference rates**, served through
Frankfurter. No key, no quota, no sign-up.

| `mode` | Returns | Use it for |
|---|---|---|
| `latest` | today's rate for each target currency | a dashboard, an invoice, a price list |
| `range` | every working day between `startDate` and `endDate` | charting a currency pair, backtesting |
| `dates` | only the specific dates in `specificDates` | converting a list of historical transactions |

`dates` is the one people build badly by hand. Accounting exports carry a
transaction date each, and looking up one rate per date sequentially is a
request per row; this batches them.

**All 30 currencies**: EUR, USD, GBP, JPY, CHF, AUD, CAD, NZD, SEK, NOK, DKK,
PLN, CZK, HUF, RON, TRY, ILS, INR, IDR, KRW, CNY, HKD, SGD, MYR, PHP, THB, ZAR,
BRL, MXN, ISK.

BGN is not in that list any more: Bulgaria adopted the euro, so the ECB stopped
publishing a lev reference rate.

**History runs back to 1999**, when the ECB series begins.

Set `baseCurrency` to anything in the list. The ECB quotes against EUR and any
other base is computed by exact division rather than a lossy round trip, so a
USD base is as accurate as a EUR one.

### Do you need an API key? No. Is there a free tier? Yes.

The two things people search for in this category, measured, are **free** and
**API key**: `exchange rate api free`, `exchange rate api key`, `exchange rate api free google`.

So, plainly:

- **No API key.** Nothing to register for, nothing to rotate, no key to leak in
  a repo. The source is a public API published deliberately by its owner.
- **No proxy setup.** The source does not bot-wall datacenter addresses, so the
  default works.
- **Pay per result, not per month.** There is no subscription and no minimum.
  Rows that error or that your filters drop are **not charged**.
- **Free to try.** Run it with the prefilled input and see real rows before
  deciding anything.

### Notes

- **Daily reference rates, not tick data.** The ECB publishes once per working day around 16:00 CET. Do not price a live trade off this; that is not what these rates are.
- The ECB quotes against EUR. Any other base is computed by exact division server-side, so a USD base is not a lossy round trip.
- Coverage is the ~30 currencies in the ECB reference set, with history back to 1999.

### FAQ

#### Do I need an API key or have I got a request quota?

No key, no quota. The ECB publishes its reference rates openly.

#### Which exchange rate should I use on an invoice?

For anything inside the EU, the ECB reference rate for the invoice date is the
figure European accounting and tax rules cite. That provenance is the reason to
use this rather than a free FX aggregator that does not name its source. Every row
carries `source: "ECB reference rate"`.

#### What happens if I ask for a weekend or a holiday?

You get the previous working day's rate, and the row tells you so: `date` is when
the ECB actually published, `requestedDate` is what you asked for, and `note`
explains the difference. Most FX APIs hand back Friday's number stamped with
Sunday's date, which is a quiet fabrication that compounds across a time series.

#### Can I get a full historical series in one run?

Yes. `mode: "range"` with `startDate` and `endDate` returns one row per date and
currency. History goes back to 1999.

#### Can I use a base currency other than EUR?

Yes. The ECB quotes against EUR, and any other base is computed by exact division
server-side, so a USD base is not a lossy round trip through two roundings.

#### Which currencies are covered?

The roughly 30 currencies in the ECB reference set. Leave `targetCurrencies`
empty to get all of them.

#### Can I use this for live trading?

No. These are daily reference rates published once per working day around 16:00
CET, not tick data. Do not price a live trade off them. That is not what they
are.

#### How do I keep an e-commerce conversion table current?

Schedule `mode: "latest"` daily, shortly after 16:00 CET. That is when the day's
rates land.

# Actor input Schema

## `mode` (type: `string`):

Latest published rates, a continuous date range, or a list of specific dates.

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

The currency everything is quoted against, e.g. USD, EUR, ILS. The ECB publishes against EUR and Frankfurter converts other bases exactly.

## `targetCurrencies` (type: `array`):

Which currencies to return, e.g. EUR, ILS, GBP. Leave empty for all 30 the ECB publishes.

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

First day of the range (YYYY-MM-DD). Used when mode is Date range.

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

Last day of the range (YYYY-MM-DD). Leave empty to run up to today.

## `specificDates` (type: `array`):

Individual dates (YYYY-MM-DD), one per line. Used when mode is Specific dates. A weekend or holiday returns the previous working day, flagged in the row.

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

Proxy settings.

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

A hard ceiling on rows for the entire run, across every target. The per-target limits above cap each one separately, so fifty targets at twenty each is still a thousand rows; this caps the total. The run stops cleanly when it is reached and nothing beyond that point is fetched or charged. Leave empty for no ceiling.

## Actor input object example

```json
{
  "mode": "latest",
  "baseCurrency": "USD",
  "targetCurrencies": [
    "EUR",
    "ILS",
    "GBP"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

ECB reference rates, flat and ready to chart or join.

# 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 = {
    "baseCurrency": "USD",
    "targetCurrencies": [
        "EUR",
        "ILS",
        "GBP"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("glitchbound/fx-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 = {
    "baseCurrency": "USD",
    "targetCurrencies": [
        "EUR",
        "ILS",
        "GBP",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("glitchbound/fx-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "baseCurrency": "USD",
  "targetCurrencies": [
    "EUR",
    "ILS",
    "GBP"
  ]
}' |
apify call glitchbound/fx-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=glitchbound/fx-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/zxhatfkSyMoYFoPa1/builds/xAW0JOPZrvRPTiuXS/openapi.json
