Quickstart
European government reference data as cited JSON. No account, no API key, no signup required — though you can have a key if you would rather pay by card. Single values cost $0.005 per call; computed answers and bulk endpoints run $0.02–$0.10 — the full table is on the pricing page.
1. Free, right now
Nothing below needs a key, a wallet or a header. Start here to see what exists:
# Every country and series this service holds
curl https://euroref.dev/catalog
# A full public-holiday calendar, cited to its official source
curl https://euroref.dev/v1/at/public-holidays
# What changed recently, and how recently each value was re-verified
curl https://euroref.dev/v1/changes
Public holidays are free for every country, permanently. So are /catalog, /v1/changes, /settlement-conventions, /openapi.json and /llms.txt.
One country is completely free — evaluate the paid surface on it
Every DE series on euroref is free: current value, history, ?as_at=, full citations. It is the same surface every paid country sits behind, so you can exercise it end to end — and check our citations against the official sources — before paying a cent:
curl https://euroref.dev/v1/de/policy-rate
curl https://euroref.dev/v1/de/vat/history
curl 'https://euroref.dev/v1/de/corporate-tax?as_at=2024-06-30'
And you can check every other country too — the numbers are not hidden
An external reviewer concluded in August 2026 that our coverage beyond the evaluation country was “asserted but externally unverified”. That was wrong, and it was our fault for never saying so plainly. The paywall covers machine access, not the numbers. For every country on euroref:
- The country pages publish each current value as HTML, free — with its citation,
effective date and last-verified date. Try
/de/policy-rate, or any country listed in/catalog. /catalogcarries the full source citation, freshness and confidence level for every series in every country, free./provenancepublishes a sha256 fingerprint of every cited source document as we archived it, free.
So audit the whole catalog against the official sources before you spend anything. What payment buys is
JSON, /history, ?as_at=, the computed answers and
certified extracts — convenience and depth, not access to secret values.
2. A paid call, and what it costs
Paid endpoints answer 402 Payment Required until they are paid. The 402 is not an error — it is the price quote, and it is machine-readable:
curl -i https://euroref.dev/v1/at/policy-rate
HTTP/1.1 402 Payment Required
payment-required: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQYXltZW50...
That header is base64-encoded JSON. Decode it to see the price, the asset, the chain and the address to pay:
curl -sD- -o /dev/null https://euroref.dev/v1/at/policy-rate \
| grep -i '^payment-required:' | cut -d' ' -f2- | base64 -d | jq .
The challenge is also mirrored into the response body, so a client that cannot read headers is not locked out. Amounts are in the asset's base units — USDC has 6 decimals, so 5000 means $0.005.
3. Two ways to pay
Option A — x402, for agents with a wallet
Pay per call in USDC on Base. No account exists to create. A client library does the 402 → sign → retry loop for you:
npm install @x402/fetch
import { wrapFetchWithPayment } from "@x402/fetch";
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const wallet = createWalletClient({ account, chain: base, transport: http() });
const fetchWithPay = wrapFetchWithPayment(fetch, wallet);
const res = await fetchWithPay("https://euroref.dev/v1/at/policy-rate");
const data = await res.json();
console.log(data.value, data.source.url); // the number, and where it came from
On success the settlement receipt comes back in the PAYMENT-RESPONSE header. You sign a payment authorisation, not a blank cheque: the amount is fixed by the challenge.
Option B — an API key, for people and businesses
Buy prepaid credit by card on the pricing page and send the key as a header. No wallet, no crypto, and you get an invoice:
curl -H 'X-API-Key: YOUR_KEY' https://euroref.dev/v1/at/policy-rate
import requests
r = requests.get(
"https://euroref.dev/v1/at/policy-rate",
headers={"X-API-Key": "YOUR_KEY"},
timeout=15,
)
r.raise_for_status()
d = r.json()
print(d["value"], d["unit"], "as at", d["effective_from"])
print("source:", d["source"]["url"])
Credit does not expire and there is no subscription. Calls that fail because of our error are not deducted. One key works on all seven regional services — credit bought on afriref.dev serves euroref, asiaref, latamref, usaref, mearef and ausref too: same key, same balance.
4. What comes back
Every paid value carries the evidence with it. The citation is the product — a number without one is what we are trying to replace:
{
"country": "AT",
"series": "policy-rate",
"value": 15.75,
"unit": "percent",
"effective_from": "2026-07-14", // when this value took legal effect
"last_confirmed": "2026-07-24", // when we last re-read the official source
"source": {
"name": "...", // the instrument, named
"url": "https://..." // the document itself
},
"confidence": "primary", // how the value was obtained
"stale": false // past its expected update cycle?
}
| Field | Why it is there |
|---|---|
effective_from | The date the value took effect, which is rarely the date we published it. Use it, not the fetch date, for anything point-in-time. Can be null: a few authorities publish a rate but never the instrument that commenced it. We store null rather than invent a date, and point-in-time reads on those series refuse with commencement_date_unknown. |
effective_from_basis | Optional. When present with the value "restatement", effective_from marks the date the value's current legal basis took effect — a new tax code, an annual finance law re-enacting an unchanged rate — not the date the value changed. The value is older; its true origin is not primary-verifiable, so the date is a verified floor rather than a change point. Some countries re-enact their entire tax code every January: without this flag, an unchanged 18% VAT stamped with the new code's date reads as a rate change that never happened. Absent on values whose effective_from is a genuine change date. |
last_confirmed | When a human or job last re-read the official source. An old date here is a disclosure, not a bug. |
source.url | The governing document. Follow it and check us. |
confidence | primary means read from the authority's own publication; also secondary, attested and derived, each explained at /catalog. Filter with /catalog?confidence=primary. Historical values reached through ?as_at= mostly do not carry their own confidence — we do not inherit the current value's, because that would claim a verification level nobody performed on the older figure. |
provenance_gap | Present only when we serve a value we cannot cite. Ninety-nine older historical rows were recorded without their own source. We serve them dated and flagged rather than either withholding them or passing them off as cited. |
stale | True when past the expected update cycle. We serve it flagged rather than withholding it or aging it quietly. |
5. Point-in-time reads
Add ?as_at=YYYY-MM-DD to get the value that was in force on a date, rather than today's:
curl 'https://euroref.dev/v1/at/policy-rate?as_at=2025-06-30'
If we hold no value in force on that date, you get a free 422 explaining what we do hold — not the nearest value, and not a guess.
The refusal names which kind of "no", because they call for different responses from you:
error | What it means |
|---|---|
no_value_in_force_on_that_date | Either the date precedes everything we hold (earliest_held tells you how far back we go), or a value covered it and lapsed with no recorded successor (lapsed_on). Both may be fixed by a backfill; both are worth asking us about. |
commencement_date_unknown | We hold a current, primary-sourced value, but the authority does not publish the date it took effect. permanent: true — no backfill can fix this, only the authority publishing a commencement date. Query without as_at for the value itself. |
absence_not_dated | There is no such tax or wage in that country, established positively rather than missing — but an absence has no commencing instrument, so we cannot confirm it held on your date too. permanent: true. |
Every one of these is free and sets no_charge: true. A refusal never costs you a call.
6. Refusals — read this before treating one as a bug
The service refuses questions it cannot answer honestly, and refusals are always free: they are validated before the payment gate, so you are never charged for a question we know we cannot answer well.
| Status | Meaning |
|---|---|
400 | Malformed input. Free. |
402 | The price quote. Pay and retry. |
404 | Unknown country or series for this brand. Each regional service serves only its own countries — check /catalog. |
422 | A refusal with a reason. Free. See the codes below. |
429 | Rate limited on a free discovery route. Retry after the header says. |
The 422 reasons you are most likely to meet:
| Code | What it means |
|---|---|
calendar_coverage_exhausted | Your date range runs past the years we hold for that country. The response names the covered period. We do not extrapolate calendars. |
calendar_not_publishable_in_advance | Permanent, flagged permanent: true. Islamic dates are confirmed by moon sighting and some calendars are gazetted annually — the year will not appear later, so do not queue a retry. |
no_value_in_force_on_that_date | An as_at date earlier than our earliest recorded value. |
no_verified_convention | A settlement question for a market whose cycle we have not verified. We will not guess a cycle. |
A refusal names the specific blocking reason and tells you what you can do instead. Treat it as data, not as failure.
7. Alerts — we watch the official sources so you don't have to
Reference values fail silently. A number that stood for years moves in one budget speech, and every hard-coded copy of the old value becomes wrong overnight — nothing crashes, nothing warns. It just starts being wrong.
A worked example from our own accuracy record: South Africa's compulsory VAT registration threshold was R1 million for roughly seventeen years — long enough to be baked into onboarding checks, software defaults and training material. On 1 April 2026 it became R2.3 million. A system with an alert learned within a day; a system with a lookup API didn't know to ask.
We fetch and hash every cited source daily, so we know when a value changes, not just what it is:
# HMAC-signed webhook the moment a series you watch changes
curl -X POST https://euroref.dev/alerts \
-H 'content-type: application/json' \
-d '{"url": "https://your-app.example/hooks/refdata",
"series": ["za/vat-registration-threshold", "ng/policy-rate", "ke/*"]}'
1–20 patterns per subscription, wildcards per country (ke/*). Payloads are signed with HMAC-SHA256 in the x-afriref-signature header; the signing secret is returned once at registration. Registering is free, and so are your first 3 deliveries — after that each delivery deducts one call from your API key (pass api_key at registration). Prefer polling? The change feed is free: /v1/changes.
Watch the documents themselves (source-change alerts): we fingerprint every official document we cite — 1,500+ gazettes, statutes, central-bank PDFs and authority pages — with SHA-256, every day, for our own verification. You can subscribe to that directly: pass sources (exact document URLs; every series response carries its source.url) instead of or alongside series, and get a signed webhook the day the bytes change — old hash, new hash, and which series cite the document. We say exactly what the event asserts: the document changed. Whether the value changed is decided by our re-verification, not by a byte diff — for value-level events, watch the series. URLs we don't fingerprint are refused free (and logged as requests). Same price, same trial, same signing as series alerts.
Secret rotation and revocation: secrets are minted server-side and never re-shown, so rotation is delete-and-re-register: DELETE /alerts/{id} authenticated with the subscription's own secret (constant-time checked; a wrong secret is indistinguishable from a missing subscription), then POST /alerts again — the new registration returns a fresh secret and the old one is dead the moment the delete lands. Two calls, no support ticket. If you cannot present the old secret (lost, not leaked), register a new subscription and simply stop honouring deliveries signed with the old one; an unpaid orphan subscription stops delivering when its trial deliveries are exhausted.
8. Rate limits
Free, unauthenticated discovery routes are throttled per IP; paid data routes are not throttled at all. Current limits and what they cover are published on /status.
9. If you are an agent, not a person
| /llms.txt | The whole service in one plain-text file, written to be read by a model. |
| /openapi.json | OpenAPI, with x-payment-info on every paid operation. |
/.well-known/x402 | x402 v2 resource manifest — every priced resource, machine-readable. |
/mcp | MCP server over streamable HTTP. Free tools: list_series, get_public_holidays. Paid: get_series, get_series_history, get_snapshot. |
Something wrong?
If a value disagrees with its official source, send us the instrument — corrections with a primary source are acted on and credited on the accuracy record. [email protected]