# Currency Exchange Rates Scraper: ECB Daily & Historical (`firebrick_zamia/currency-exchange-rates`) Actor

Currency exchange rates from the free ECB euro reference rates (optional Frankfurter API): latest, a date or a date range, any base currency via EUR cross rates, amount conversion. About 30 currencies, keyless, official sources.

- **URL**: https://apify.com/firebrick_zamia/currency-exchange-rates.md
- **Developed by:** [Aadam Ahmed](https://apify.com/firebrick_zamia) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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

## Currency Exchange Rates Scraper: ECB Daily and Historical Rates

**Get euro foreign exchange reference rates from the European Central Bank, latest, for one date or a date range, converted to any base currency via EUR cross rates, with optional amount conversion. Free, keyless official source; optional Frankfurter API (ECB-based). One row per date and currency pair.**

### What you get

Rows of `{date, base, quote, rate, amount, converted, source}` for about 30 currencies. For a date that has no publication, `date` is the previous business day actually used and `requested_date` shows what you asked for.

### Use cases

- Convert invoices, prices or reports into a reporting currency at the official ECB reference rate of a given day.
- Pull historical rate series for a base currency (for example USD to EUR, GBP, JPY) for accounting, research or dashboards.
- Give an AI agent a trustworthy, source-labelled exchange rate (callable through Apify's MCP server, https://mcp.apify.com).
- Schedule a daily run to keep a spreadsheet or database of rates current.

### What it does

- **Primary source, `ecb`**: reads the ECB's public files `eurofxref-daily.xml` (latest), `eurofxref-hist-90d.xml` (last 90 days) and `eurofxref-hist.xml` (full history since 4 January 1999) from `https://www.ecb.europa.eu/stats/eurofxref/`. The ECB's files are euro-based: each rate is units of currency per 1 EUR.
- **Cross rates**: for another base, rate(base to quote) = rate(EUR to quote) / rate(EUR to base). The conversion is calculated here, not published by the ECB, and is rounded to 10 significant digits.
- **Optional source, `frankfurter`**: the free Frankfurter API (`https://api.frankfurter.app`), which republishes ECB data and converts bases server-side. For long ranges it may return sampled (weekly or monthly) points; see its documentation.
- Polite by design: sequential requests at most 1 per second, an identifying User-Agent, retries with exponential backoff on HTTP 429 and 5xx, and clear failure messages. No proxies or workarounds.

### Input

| Field | Meaning |
|---|---|
| `mode` | `latest` (default), `date` or `range` |
| `base` | ISO 4217 code to convert from (default `EUR`) |
| `targets` | Codes to convert to; empty means every currency published that day |
| `date` | Date mode, `YYYY-MM-DD` (previous business day is used if none was published) |
| `startDate`, `endDate` | Range mode; empty end date means up to the latest day |
| `amount` | Amount of base currency to convert (default 1) |
| `source` | `ecb` (default) or `frankfurter` |
| `maxRows` | Safety cap on rows (default 1,000) |

### Example input and output

Input (bundled `test-input.json`): USD base, targets EUR, GBP and JPY, range 2026-09-01 to 2026-09-05, amount 100.

The record below uses **illustrative values only** (not a real quote); real rates are whatever the ECB published for that day. Our tests use synthetic files in the ECB XML format.

```json
{
  "date": "2026-09-01", "requested_date": null, "base": "USD", "quote": "GBP",
  "rate": 0.75, "amount": 100, "converted": 75, "source": "ecb"
}
```

### Limitations (read before relying on it)

- **Publication schedule**: the ECB publishes around 16:00 CET on TARGET business days. There are no rates for weekends and TARGET holidays, so a range returns only business days and a single date falls back to the previous one. Rates for "today" do not exist before the afternoon publication.
- **About 30 currencies only.** The set is the ECB's and changes over time (some currencies were discontinued). A requested currency that the ECB does not publish makes the run fail with the list of available codes; nothing is estimated.
- **The UAE dirham (AED) is not published by the ECB**, nor are the other Gulf currencies. This actor does not fake them and does not compute AED from the USD peg; if you need AED, use a source that publishes it.
- **Reference rates are for information only**, not for transactions or settlement. They are not tradeable, bid/ask or intraday rates, and cross rates are derived. Check the ECB's terms of use for reuse and attribution; this is not financial advice.
- Rounding: rates carry up to 10 significant digits; converted amounts are the rate times the amount, with the same precision.
- Not affiliated with the European Central Bank or Frankfurter.

### Pricing (pay per result)

Pay-per-event with Apify's built-in dataset-item event: each row (one date and currency pair) is one result. The live price is set in Apify Console and shown on the Store page. Planning figure in our notes: USD 1.00 per 1,000 results (not yet published).

### FAQ

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

**Why is the date different from the one I asked for?** The ECB did not publish that day (weekend or holiday), so the previous business day is returned; `requested_date` keeps your date.

**Can I get AED, SAR or other Gulf currencies?** Not from the ECB. See limitations.

**How is USD to GBP calculated?** (GBP per EUR) / (USD per EUR) on the same date.

**Which source should I use?** `ecb` is the primary and reads the official files. Use `frankfurter` if you prefer its server-side conversion.

# Actor input Schema

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

latest: the most recent published rates. date: the rates for one day. range: every published day between two dates.

## `base` (type: `string`):

ISO 4217 code of the currency you convert from, for example EUR, USD or GBP. Non-EUR bases are computed as cross rates via EUR.

## `targets` (type: `array`):

ISO 4217 codes to convert to, one per line. Leave empty for every currency published for that day (about 30).

## `date` (type: `string`):

Date mode: day in YYYY-MM-DD. If the ECB published nothing that day (weekend, TARGET holiday), the previous business day is returned and marked in rate_date.

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

Range mode: first day in YYYY-MM-DD.

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

Range mode: last day in YYYY-MM-DD. Empty means up to the latest published day.

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

Amount of the base currency to convert. Each row gets a converted value (rate times amount). Default 1.

## `source` (type: `string`):

ecb reads the official ECB files directly (primary). frankfurter uses the free Frankfurter API, which republishes the same ECB rates and converts base currencies server-side.

## `maxRows` (type: `integer`):

Safety cap on the number of rows (date x currency pairs) returned. Each row is one billed result.

## Actor input object example

```json
{
  "mode": "latest",
  "base": "USD",
  "targets": [
    "EUR",
    "GBP",
    "JPY"
  ],
  "amount": 1,
  "source": "ecb",
  "maxRows": 1000
}
```

# Actor output Schema

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

All result records.

# 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 = {
    "base": "USD",
    "targets": [
        "EUR",
        "GBP",
        "JPY"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("firebrick_zamia/currency-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 = {
    "base": "USD",
    "targets": [
        "EUR",
        "GBP",
        "JPY",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("firebrick_zamia/currency-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 '{
  "base": "USD",
  "targets": [
    "EUR",
    "GBP",
    "JPY"
  ]
}' |
apify call firebrick_zamia/currency-exchange-rates --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,firebrick_zamia/currency-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/eASXtUsIEJySscOcT/builds/rwHSOtfCPIvPouLfx/openapi.json
