# ECB Euro Exchange Rates Scraper — Official FX Reference Rates (`devilscrapes/ecb-euro-exchange-rates-scraper`) Actor

Pull official European Central Bank euro reference exchange rates from the ECB free, keyless SDMX Data API — daily, monthly, quarterly, or annual rates for any currency the ECB publishes, flattened into clean one-row-per-observation records. No API key, no subscription.

- **URL**: https://apify.com/devilscrapes/ecb-euro-exchange-rates-scraper.md
- **Developed by:** [DevilScrapes](https://apify.com/devilscrapes) (community)
- **Categories:** 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.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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

<div align="center">
  <img src=".actor/icon.svg" width="160" alt="Devil Scrapes mark" />

## ECB Euro Exchange Rates Scraper

**💰 $2.20 / 1 000 results**  ·  pay only for results  ·  no credit card to try

*The devil's in the data — every rate traced back to the ECB's own reference series.* 😈

Pull official European Central Bank euro reference exchange rates from the ECB's free, keyless SDMX Data API — daily, monthly, quarterly, or annual rates for any currency the ECB publishes — flattened into clean, one-row-per-observation records.

</div>

***

### 🎯 What this scrapes

This Actor queries the **ECB's SDMX 2.1 Data API** (`data-api.ecb.europa.eu`,
the `EXR` reference-rate dataflow) and returns one structured row per
`(currency, date)` observation: date, currency, the EUR-denominated rate,
frequency, and the ECB's own quality/status flags.

The ECB doesn't serve a flat table — it serves an **index-keyed SDMX-JSON
cube** that has to be joined against a separate `structure` block to mean
anything, and the series order the API returns is **not** guaranteed to match
the order you requested. Getting that join right — and never silently
attaching the wrong rate to the wrong currency — is the actual work this
Actor sells.

### 🔥 What we handle for you

- 🛡️ **We rotate browser fingerprints** (curl-cffi impersonation) so every request looks like a real browser, not Python.
- 🔁 **We retry with exponential backoff** on `408 / 429 / 503` and honour `Retry-After` — up to 5 attempts per page.
- 🌐 **We rotate residential proxies** through Apify Proxy on every retry — fresh session, fresh exit IP.
- 🧱 **We isolate per-series/per-observation failures** — one malformed series is skipped and logged, the run keeps going.
- 🧊 **We keep the dataset clean** — Pydantic-validated rows, nullable fields typed correctly, dimensions resolved by index rather than assumed request order.
- 💰 **You pay only for observations that land.** A query for a window with no published rates still succeeds — you're never charged for a failed run.

### 💡 Use cases

- **Fintech & payments** — reconcile transactions against the official EUR reference rate for a given settlement date.
- **Treasury / FP\&A** — pull historical daily/monthly rates for multi-currency reporting and budgeting.
- **Accounting & invoicing tools** — attach an authoritative, audit-defensible FX rate to a cross-border invoice.
- **Econ research** — build long-run time series of ECB reference rates for any published currency.
- **Compliance / audit** — cite the exact ECB rate (and its status flag) used to convert a historical transaction.

### ⚙️ How to use it

1. Click **Try for free** at the top of the page.
2. Set **Currencies** — ISO-4217 codes against EUR, e.g. `USD`, `GBP`.
3. Pick a **Frequency** — Daily, Monthly, Quarterly, or Annual.
4. Optionally set **Start date** / **End date** (`YYYY-MM-DD`). Leave blank to get the latest published rates.
5. Set **Max results** (up to 50,000).
6. Click **Start**. Rows stream into the run's dataset as each series/observation is resolved.
7. Export from **Storage → Dataset** as JSON, CSV, or Excel — or fetch via the API.

### 📥 Input

| Field | Type | Required | Default | Notes |
|---|---|:--:|---|---|
| `currencies` | `array<string>` | no | `["USD"]` | ISO-4217 currency codes against EUR. At least one. |
| `frequency` | `string` | no | `"D"` | One of `D` (daily), `M` (monthly), `Q` (quarterly), `A` (annual). |
| `startDate` | `string` | no | — | `YYYY-MM-DD`. Omit for the ECB API's default window. |
| `endDate` | `string` | no | — | `YYYY-MM-DD`. Omit to fetch through the latest published observation. |
| `maxResults` | `integer` | no | `1000` | Cap on total observation rows across all requested series (1-50,000). |
| `proxyConfiguration` | `object` | no | `{"useApifyProxy": true}` | Apify Proxy configuration. |

#### Example input

```json
{
  "currencies": ["USD", "GBP"],
  "frequency": "D",
  "maxResults": 1000,
  "proxyConfiguration": {"useApifyProxy": true}
}
```

### 📤 Output

Every row is one `(currency, date)` exchange-rate observation.

| Field | Type | Notes |
|---|---|---|
| `date` | `string` | Observation date (`YYYY-MM-DD`). |
| `currency` | `string` | Resolved currency code (e.g. `"USD"`). |
| `currency_denom` | `string` | Denominating currency — always `"EUR"` for the `EXR` dataflow. |
| `rate` | `number \| null` | The exchange rate, when published. |
| `frequency` | `string` | Resolved frequency (`D`, `M`, `Q`, or `A`). |
| `exr_type` | `string` | ECB exchange-rate type (e.g. `"SP00"` — spot rate). |
| `exr_suffix` | `string` | ECB series-variation suffix (e.g. `"A"` — average). |
| `obs_status` | `string \| null` | ECB observation status/quality flag, when published. |
| `series_key` | `string` | Raw SDMX series key, for traceability. |

#### Example output

```json
{
  "date": "2024-01-02",
  "currency": "USD",
  "currency_denom": "EUR",
  "rate": 1.0956,
  "frequency": "D",
  "exr_type": "SP00",
  "exr_suffix": "A",
  "obs_status": "A",
  "series_key": "0:1:0:0:0"
}
```

### 💰 Pricing

Pay-Per-Event — you pay only when these events fire:

| Event | USD | What it is |
|---|---:|---|
| `actor-start` | $0.20 | One-off warm-up charge per run |
| `observation-scraped` | $0.002 | Per `(currency, date)` exchange-rate row written to the dataset |

Example: 100 observations ≈ **$0.40** total per run; at 1,000 rows that's
**$2.20/1,000** ($0.20 start + 1000 x $0.002). No subscription, no
minimum, no card to start — Apify gives every new account free credit.

### 🚧 Limitations

This Actor covers the ECB's **`EXR` reference-rate dataflow only** — other
ECB dataflows (BSI, ICP, YC, …) are out of scope for v1. It publishes the
ECB's official reference fix, not live/intraday market quotes, and only
currencies the ECB itself publishes (cross-rates the ECB does not publish,
e.g. USD/GBP, are not derived). Non-trading days (weekends, TARGET
holidays) simply have no observation — row counts do not equal the number
of calendar days requested.

### ❓ FAQ

**Does this need an ECB account or API key?**

No. The ECB's SDMX Data API is free and keyless.

**Why doesn't the row count match the number of days I requested?**

The ECB doesn't publish a rate on weekends or TARGET holidays — those
dates simply have no observation in the response.

**What happens if my date window has no published rates?**

The run finishes successfully with zero rows and a status message
describing the query — you're not charged for a failed run.

**Can I get rates between two non-EUR currencies (e.g. USD/GBP)?**

Not in v1 — only rates the ECB itself publishes against EUR. Deriving
cross-rates is a candidate for a future version.

### 💬 Your feedback

Spotted a bug, hit a weird edge case, or need another ECB dataflow
covered? Open an issue on the Actor's **Issues** tab on Apify Console —
we ship fixes weekly and we read every report.

***

<div align="center">

Built by **[Devil Scrapes](https://apify.com/DevilScrapes)** 😈 — a small fleet of
opinionated public-data Actors. Honest pricing, real engineering, zero fine print.

</div>

# Actor input Schema

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

ISO-4217 currency codes to fetch against EUR (e.g. "USD", "GBP"). At least one is required.

## `frequency` (type: `string`):

ECB observation frequency: Daily, Monthly, Quarterly, or Annual.

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

YYYY-MM-DD. Leave blank to let the ECB API decide the window's start — an unbounded window always returns the latest published rates, so it can't go stale.

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

YYYY-MM-DD. Leave blank to fetch through the most recently published observation.

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

Cap on total observation rows collected across all requested series.

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

Apify Proxy configuration. The ECB Data API is a public keyless endpoint, but Apify Proxy is attached per house rule.

## Actor input object example

```json
{
  "currencies": [
    "USD",
    "GBP"
  ],
  "frequency": "D",
  "maxResults": 1000,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `datasetItems` (type: `string`):

All dataset items as JSON.

## `datasetItemsCsv` (type: `string`):

Same data exported to CSV.

## `datasetView` (type: `string`):

Open the run dataset in the Console.

# 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 = {
    "currencies": [
        "USD",
        "GBP"
    ],
    "frequency": "D",
    "maxResults": 1000,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("devilscrapes/ecb-euro-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 = {
    "currencies": [
        "USD",
        "GBP",
    ],
    "frequency": "D",
    "maxResults": 1000,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("devilscrapes/ecb-euro-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 '{
  "currencies": [
    "USD",
    "GBP"
  ],
  "frequency": "D",
  "maxResults": 1000,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call devilscrapes/ecb-euro-exchange-rates-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,devilscrapes/ecb-euro-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/hceGUK9omr0je2tjY/builds/iWtL1swXgybCipvLi/openapi.json
