How to use it: 3 steps
- Make a free Apify accountApify is the cloud platform where our tools run. The free plan gives you $5 of usage every month, no credit card needed. You pay Apify, not us.
- Paste your input and press StartNo code needed: you fill in a form (the input). It already has a working example. For example:
EUR/USDin Currency pairs. Each start is one run. - Download the resultsWhen the run ends, the results are in a table (the dataset). Open Output, press Export and pick Excel or CSV. JSON and the API are there too.
This tool is an Apify Actor: a small program that runs on Apify. You can also run it on a schedule, for example every Monday. New to it? Read how it works, step by step and the 8 words you need first.
What it does
Get official European Central Bank (ECB) exchange rates for any currency pair and any date since 1999, and convert amounts with them. Add a pair like USD/JPY and a date, and you get one clear row per pair and day. Weekends, ECB holidays and days not published yet are marked, not hidden. No API key is needed, and a typical run (2 pairs, last 7 days) costs about $0.01.
- Any pair. The ECB publishes rates against the euro. For pairs without the euro, like
USD/JPY, the Actor computes the cross rate from the two euro rates and shows both of them (eurToBase,eurToQuote). You can check the math yourself. - Any date since 1999-01-04. You can use a list of dates, a date range or the last N days.
- Every day explained. A day without a rate has
status: no_rateand a reason innote:weekend,ecb_holiday,not_yet_publishedorcurrency_not_quoted. - Optional fill. With
missingDatePolicy: last_published, a missing day gets the last published rate before it.effectiveDatealways shows which day's rate was used. - Conversion. Add amounts and get
convertedAmount= amount × rate, rounded the way you choose.
Good for
- Accounting and invoices: the official ECB rate for the invoice date.
- Finance and audit: a full rate history for a pair, with holidays clearly marked.
- Reports and BI: a clean daily table of rates for several pairs.
Not a good fit
- No live or intraday rates. The ECB publishes once a day; today's rate appears after about 16:00 CET.
- About 30 currencies are quoted today (45 in the full history). No crypto, no exotic currencies outside the ECB list.
- These are reference rates for information. They are not bank buy/sell rates.
Who uses it
Accountants
Get the official ECB rate for each invoice date.
Finance and audit teams
Build a rate history for a pair with holidays marked.
BI analysts
Get a clean daily table of rates for several pairs.
Price
Pay per event: you pay for each result the tool saves, plus a small start fee for each run. The price includes Apify platform costs. Apify bills you, not us. Prices below are for the Apify free plan.
| What you pay for | Price | When |
|---|---|---|
| Start fee | $0.002 | Charged once per run. This tool uses 1 GB of memory or less by default; more memory costs more. |
| Exchange rate row | $0.0009 (from $0.00075 on paid Apify plans) | One row with an official ECB rate (direct, inverse or cross) for one pair, date and amount, saved to the Dataset. |
| Row without a rate | $0.00003 | One row that explains why there is no rate (weekend, holiday, not yet published, currency not quoted or not supported) or a same-currency pair. Dates the source failed for are not written or charged. |
Examples
- One rate row: about $0.0029.
- 1,000 rate rows: about $0.90.
- Every day 100 rate rows, for 30 days: about $2.76 a month.
Free to try: the Apify free plan gives you $5 of usage every month, no credit card needed. That is enough for about 5,500 rate rows.
Prices checked on 24 October 2026. See the current price on Apify.
Questions
Why is there no rate on Saturday?
The ECB does not publish on weekends and its holidays. The row says why.
Are these bank rates?
No. They are reference rates for information, not buy or sell rates.
Can I get USD to JPY?
Yes. The tool computes the cross rate from two euro rates and shows both.
Does it have crypto?
No. Only about 30 currencies from the ECB list.
Do I need an API key?
No. The Actor reads public ECB data.
How fresh is the data?
The Actor reads the source on every run. providerLastPublished shows the latest ECB publication at that time.
Details
Step by step on Apify
- Open the Input tab.
- In Currency pairs (
pairs), keep the prefillEUR/USDandEUR/GBPor add your own, for exampleUSD/JPY. - Keep Last days (
lastDays) at 7, or add Specific dates (dates), for example2024-12-31. - Click Start.
- Open the Output tab. Each row has
pair,requestedDate,status,rateandnote.
Tips for better results
- Use the last published option to fill weekends with Friday's rate.
- Check effectiveDate. It shows which day's rate was used.
- Run after about 16:00 CET to get today's rate.
Limits and good to know
- Platform usage (compute, storage) is included. You do not pay it
- Rows from
missingDatePolicy: skipare not written and not charged - If the source fails, no rows are written for the failed dates, so they are not charged
- An invalid input costs only the Actor start
- If you set a maximum cost for the run, the Actor stops writing rows before it goes over it. The summary says
CAPPED - Up to 10 pairs, 100 dates or a 366-day range, 10 amounts and 10,000 rows per run
- The largest tested run (10,000 rows) took about 30 seconds
- Dates from 1999-01-04 to today (UTC)
For developers
Sample result
One result from a real test run, in JSON. Real values change from run to run. In Excel or CSV each field is a column.
{
"pair": "USD/JPY",
"base": "USD",
"quote": "JPY",
"requestedDate": "2024-12-31",
"effectiveDate": "2024-12-31",
"status": "ok",
"note": null,
"noteText": null,
"rate": 156.9544711,
"inverseRate": 0.006371274377,
"rateType": "cross_via_EUR",
"eurToBase": 1.0389,
"eurToQuote": 163.06,
"amount": 1000,
"convertedAmount": 156954.47,
"provider": "ECB",
"providerName": "European Central Bank",
"providerLastPublished": "2026-10-09",
"source": "Frankfurter API v2 (frankfurter.dev)",
"sourceUrl": "https://api.frankfurter.dev/v2/rates?base=EUR"es=GBP%2CJPY%2CUSD&providers=ECB&from=2024-12-21&to=2024-12-31",
"fetchedAt": "2026-10-12T10:48:11.221Z"
}Use it from code
Actor ID: cliqtomedia/historical-fx-rates-converter. You need your Apify API token from Apify Console → Settings → API & Integrations.
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("cliqtomedia/historical-fx-rates-converter").call(run_input={
"pairs": [
"EUR/USD",
"EUR/GBP"
],
"lastDays": 7
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item)curl -X POST "https://api.apify.com/v2/acts/cliqtomedia~historical-fx-rates-converter/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"pairs": ["EUR/USD", "EUR/GBP"], "lastDays": 7}'# pip install langchain-apify
# set APIFY_TOKEN in your environment
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("cliqtomedia/historical-fx-rates-converter")
result = tool.invoke({"run_input": {"pairs": ["EUR/USD", "EUR/GBP"], "lastDays": 7}})
print(result)The cURL call waits for the results, up to 300 seconds. For longer runs, start a run (/runs) and get the results with a webhook or by checking the run. Apify API docs. LlamaIndex, MCP for AI agents and the JavaScript client: AI agents & MCP.
Data fields (21)
| Field | Type | Meaning |
|---|---|---|
pair | string | Pair as BASE/QUOTE, e.g. EUR/USD. |
base | string | Base currency (ISO 4217). |
quote | string | Quote currency (ISO 4217). |
requestedDate | string | Date you asked for (YYYY-MM-DD). |
effectiveDate | string | Publication date the rate comes from. Differs from requestedDate only with last_published; empty when there is no rate. |
status | string | Row result: ok, no_rate or unsupported_currency. Dates the source failed for get no rows; they are listed in RUN_SUMMARY (sourceError). |
note | string | Machine-readable reason: weekend, ecb_holiday, not_yet_published, currency_not_quoted, last_published_used, same_currency or unsupported_currency. Empty for a normal published rate. |
noteText | string | The same reason in plain English. |
rate | number | Units of QUOTE for 1 BASE on effectiveDate. ECB value as published for EUR/X; exact computation from the two ECB euro rates otherwise (10 significant digits). |
inverseRate | number | Units of BASE for 1 QUOTE (1 / rate, 10 significant digits). |
rateType | string | How the rate was obtained: direct (EUR/X as published), inverse (X/EUR), cross_via_EUR (X/Y from both euro rates) or identity (X/X). |
eurToBase | number | ECB euro reference rate of the base currency on effectiveDate (1 for EUR). |
eurToQuote | number | ECB euro reference rate of the quote currency on effectiveDate (1 for EUR). |
amount | number | Amount in BASE you asked to convert; empty when no amounts were given. |
convertedAmount | number | amount × rate in QUOTE, rounded to the chosen decimals. |
provider | string | Rate provider key, e.g. ECB. |
providerName | string | Rate provider name. |
providerLastPublished | string | Latest publication date the provider had at run time. |
source | string | Data access route (Frankfurter API v2). |
sourceUrl | string | API request that returned the rates for this row (or the provider currency list for unsupported codes). |
fetchedAt | string | When the run fetched the data (ISO 8601, UTC). |
Input settings (9)
The main fields of the input form. With the API you send them as JSON.
| Setting | Name in the API | Type | Example value |
|---|---|---|---|
| Currency pairs required | pairs | array | ["EUR/USD", "EUR/GBP"] |
| Specific dates | dates | array | |
| Date from | dateFrom | string | |
| Date to | dateTo | string | |
| Last days | lastDays | integer | 7 |
| Rate provider | provider | string | "ECB" |
| Days without a rate | missingDatePolicy | string | "none" |
| Amounts to convert | amounts | array | |
| Decimals for converted amounts | rounding | integer | 2 |
Use cases with this tool
Each guide shows all the steps and the other tools.




