NBP & ECB Exchange Rates - Historical Currency Converter
Pricing
from $2.00 / 1,000 amount converteds
NBP & ECB Exchange Rates - Historical Currency Converter
Get NBP and ECB reference exchange rates, look up historical dates and convert currency amounts. Each result includes the rate date for traceable reporting. Uses public central-bank APIs, not scraped prices. Not a live trading feed or accounting advice.
Pricing
from $2.00 / 1,000 amount converteds
Rating
0.0
(0)
Developer
Daniel Michałek
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Official FX Rates (NBP + ECB)
Get official exchange rates straight from the public APIs of Narodowy Bank Polski (NBP) and the European Central Bank (ECB), and convert amounts with the correct rate date - including weekend/holiday look-back and the Polish tax rule "NBP mid rate from the last business day before the transaction" (D-1).
Typical uses: invoice and expense conversion for Polish bookkeeping (VAT, CIT/PIT), EUR reference rates for contracts and reports, historical rate series for analysis.
Source: European Central Bank, ECB Data Portal API.
ECB source data is available free of charge. Actor charges pay for the normalization,
conversion and delivery service, not for access to ECB data. See the
ECB copyright and reuse notice.
Converted amounts and cross rates are calculated by this Actor; published source observations
are unchanged. Every ECB dataset row and run summary includes this disclosure in sourceNotice.
NBP values come from the official NBP Web API.
What it does
- Validates your input and plans the minimum number of API calls (NBP ranges are split into 93-day chunks, the NBP limit; ECB into 366-day chunks).
- Calls only these official endpoints:
- NBP:
https://api.nbp.pl/api/exchangerates/...(JSON) - ECB:
https://data-api.ecb.europa.eu/service/data/EXR/...(SDMX CSV)
- NBP:
- Writes one dataset item per published rate (rate list) and one per requested conversion.
Rate semantics of every rate item: 1 baseCurrency = rate quoteCurrency.
- NBP (tables A/B, mid rates): base = foreign currency, quote = PLN.
- ECB (daily euro reference rates): base = EUR, quote = foreign currency.
- Conversions between two non-base currencies are computed as cross rates via PLN (NBP) or EUR (ECB), using rates of the same publication date.
Input
| Field | Type | Default | Description |
|---|---|---|---|
source | NBP or ECB | NBP | Which central bank to use. |
nbpTable | A, B or C | A | NBP only. A = major currencies (daily), B = other currencies (weekly), C = bid/ask (rate list only, not for conversions). |
currencies | array of ISO codes | - | Currencies to list rates for. The base currency (PLN / EUR) is ignored. |
startDate | YYYY-MM-DD | - | Start of the rate list. Required if currencies is set. |
endDate | YYYY-MM-DD | startDate | End of the rate list (inclusive, max 3660 days). |
conversions | array | - | { "amount": 100, "from": "USD", "to": "PLN", "date": "2024-01-02", "id": "optional" }, max 10000 per run. |
rateDateMode | exact, onOrBefore, before | onOrBefore | onOrBefore: rate of that day or the last one published before it. before: last rate strictly before the date (Polish D-1 tax rule). exact: only a rate published on that day. |
maxLookbackDays | integer 0-31 | 10 | How far back a conversion may look for the last published rate. |
decimals | integer 0-8 | 2 | Rounding of converted amounts (half away from zero). |
You need at least currencies + startDate, or conversions (or both).
Example - invoice conversion with the Polish D-1 rule:
{"source": "NBP","rateDateMode": "before","conversions": [{ "id": "FV/2024/001", "amount": 1200, "from": "EUR", "to": "PLN", "date": "2024-01-03" },{ "id": "FV/2024/002", "amount": 99.99, "from": "USD", "to": "EUR", "date": "2024-01-08" }]}
Example - ECB reference rates for January 2024:
{"source": "ECB","currencies": ["USD", "PLN", "CHF"],"startDate": "2024-01-01","endDate": "2024-01-31"}
Output
Numbers in the examples below are illustrative. Rate item:
{"recordType": "rate","source": "NBP","date": "2024-01-02","currency": "EUR","baseCurrency": "EUR","quoteCurrency": "PLN","rate": 4.3434,"table": "A","tableNo": "001/A/NBP/2024"}
NBP table C items carry bid and ask instead of rate (rate is null).
Conversion item:
{"recordType": "conversion","id": "FV/2024/001","source": "NBP","amount": 1200,"from": "EUR","to": "PLN","requestedDate": "2024-01-03","rateDateMode": "before","status": "ok","rateDate": "2024-01-02","rate": 4.3434,"converted": 5212.08,"path": "direct"}
path is direct, cross via PLN / cross via EUR, or identity (same currency). A conversion
that cannot be served has status: "error" and error:
RATE_NOT_FOUND- no rate was published within the look-back window (or not on the exact day).UPSTREAM_INCOMPLETE- an API request covering that date failed; the Actor refuses to guess a rate from partial data.
A run summary (source, planned requests, upstream errors, counts, charged events,
stoppedByChargeLimit) is stored in the default key-value store under OUTPUT. If the API fails
and nothing billable could be produced, the run is marked as failed.
Pricing (pay per event)
| Event | Charged when |
|---|---|
rate-record | once per rate item written to the dataset |
amount-converted | once per conversion with status: "ok" |
Conversions with status: "error" are returned but never charged. Invalid input fails the run
before any API call or charge. The price per event is shown on the Actor's Pricing tab in
Apify Store. If your maximum charge per run is reached, the Actor stops after the last paid item
and sets stoppedByChargeLimit: true in OUTPUT.
The existing stop policy is conservative: reaching the limit for one event type stops the
whole run, even if a cheaper event type could still fit within the remaining budget.
The Actor checks capacity before each charge and publishes a paid item only after the SDK
confirms chargedCount: 1. A zero budget or declined charge publishes no paid item. In a
run without pay-per-event pricing, output is still available and chargedEvents remains zero.
This Actor expects the two named events above; automatic dataset-item pricing is rejected
because it would also charge conversion errors.
Billing and dataset writes are separate operations, with no shared transaction. Actor.charge
does not expose a persistent caller idempotency key for replay after a process restart; the
underlying client generates a key for retries within a single API call. A failed write can
leave a confirmed charge without an output item; a lost
charge acknowledgement can leave an unknown billing result. A durable FX-RUN-STATE record
therefore blocks automatic replay after interruption and reports RECONCILIATION_REQUIRED.
Inspect the saved phase and confirmed counters against run billing and dataset records before
recovery; do not delete the guard or start with fresh storage as a retry. Completed runs with
the same input return the saved summary without charging or publishing again. This is not an
exactly-once guarantee and does not support concurrent processes sharing the same storage.
Limitations
- No scraping. Data comes only from the official public NBP Web API and ECB Data Portal API listed above; no web pages are parsed and no third-party rate providers are used.
- Rates are the official published values. NBP mid rates start on 2002-01-02, ECB reference rates on 1999-01-04; earlier dates are rejected.
- NBP table B is published weekly; use a longer
maxLookbackDaysif needed. Currencies that are not in the selected table simply return no data. - ECB reference rates are published around 16:00 CET on TARGET business days; NBP table A on
Polish business days. Same-day requests made before publication fall back according to
rateDateMode. - Cross rates are derived arithmetically from two official rates of the same day; they are not themselves official quotes.
- Results depend on the availability of the upstream APIs. Transient errors (HTTP 429/5xx, network errors) are retried up to 3 times with back-off.
- This is data tooling, not tax or legal advice.
Local recovery candidate
This copy preserves the existing Actor identity and ownership. Source corrections have not
been deployed to the live Actor. The local candidate package is version 0.1.1, targeting
Node.js 22 or newer. Output links expose the dataset and OUTPUT summary; the dataset view
includes the ECB disclosure. Dockerfile and .dockerignore were newly authored on
2026-10-21 because the remote Dockerfile could not be read; they are not recovered originals.
The local Node 22 container build and an offline startup smoke test passed with the
preserved proxy-CA Dockerfile; see evidence/docker-validation.json. Production packaging
uses ordinary TLS-verified npm ci and prints runtime file hashes during the build.
The native import package and expected hashes are under evidence/release-import-0.1.1/.
Packaging was prepared without repeating those tests; hosted build, billing and deployment
remain separate checks.
Recovery provenance remains in the parent RECOVERY_MANIFEST.json; combined validation
evidence is in evidence/fx-validation.json.