API documentation
Autocompletion, validation and geocoding of addresses in Hungary through a REST API, with JSON responses.
In brief
The Adatka API is available at https://adatka.hu over HTTPS and responds in JSON format, UTF-8 encoded.
| Endpoint | What it is for |
|---|---|
GET /suggest | address suggestions while typing, also with step-by-step selection |
GET /reverse | address from coordinates |
POST /clean | validation and standardisation of an address list, at most 1,000 rows per request |
GET /health | status of the service |
/suggest, /reverse and /clean return a uniform address object: the parts of the address, the administrative data, the coordinates, the match level and the sources used.
The service is currently available as an invitation-only beta, free of charge.
Quick start
The commands are written for a Linux or macOS terminal (bash, zsh). You can request an API key at the bottom of this page, in the “Access” section. During the beta, the service also responds without a key for the time being: if you do not have a key yet, skip entering it — the commands run anyway (details: API key).
If you have a key, enter it first: paste the line below and press Enter; after the prompt “Kulcs:” (Hungarian for “Key:”), paste the key and press Enter again. The key you type is not shown on the screen.
printf "Kulcs: "; read -rs ADATKA_KEY; export ADATKA_KEY; echoWith the first request you get three suggestions for the text entered:
curl -sG https://adatka.hu/suggest -H "Authorization: Bearer $ADATKA_KEY" --data-urlencode "q=szeged tisza lajos krt 2" --data-urlencode "limit=3"Expected result JSON; three addresses in the suggestions list, the first: 6720 Szeged, Tisza Lajos körút 2 ("match_level": "house").
Tip: the response is easier to read if you add this to the end of the command: | python3 -m json.tool.
API key
Send the key in a header of every request; the two forms are equivalent:
Authorization: Bearer <API-key>
X-API-Key: <API-key>A key sent in the URL is ignored, because URLs remain in logs and in the browser history; such a request counts as a request without a key.
There are two kinds of key:
| Property | Server key | Browser key |
|---|---|---|
| Where it can be called from | from your server | from a website, in the visitor’s browser |
| Endpoints | /suggest, /reverse, /clean | only /suggest |
| What we reject | requests coming from a browser (those with an Origin header) | requests from a website not assigned to the key (judged by the Origin header or, if it is missing, the Referer header) |
| Default limit | 120 requests per minute | 120 requests per minute and 2,000 per day, both per visitor (per IP address) |
The server key is a secret: it must not end up in code sent to the browser (HTML, JavaScript), in a Git repository, in a URL or in a log. If it has fallen into the wrong hands, write immediately to info@adatka.hu, preferably from the email address to which we sent the key. We will revoke the key and issue a new one in its place. When requesting a browser key, give the exact web address of the website, including the https:// prefix; if the website also opens without www, give both variants.
The endpoints can also be called directly from a browser: responses carry the Access-Control-Allow-Origin: * header, and the preflight request allows the GET and POST methods and any request header. From a website, call with a browser key; the server key does not belong in the browser.
During the beta, the service also responds without a key for the time being, but it always rejects an invalid or revoked key (401). This makes it easy to check whether your key reaches us: the command below appends a letter (x) to the end of the key — you should get a 401 response.
curl -sG https://adatka.hu/suggest -H "Authorization: Bearer ${ADATKA_KEY:?nincs megadva}x" --data-urlencode "q=Szeged" -o /dev/null -w "%{http_code}\n"Expected result 401. If you get 200 instead, the key does not reach us: check how the header is written. If you have not entered the key, the command does not run, and the terminal prints an error message ending with: ADATKA_KEY: nincs megadva (Hungarian for “not set”).
Send the key with every request from now on: this way we assign your requests to your key, and if the key becomes mandatory, you will not need to change anything. We will notify API key holders of this in advance, by email. You can request a key at the bottom of this page, in the “Access” section.
Address suggestions — GET /suggest
Address suggestions while typing, based on the text entered in the address field; the first suggestion is the best match. The text may be incomplete, without accents, abbreviated (“bp”, “krt”, “u.”) or mistyped, and the postal code may stand at the beginning or at the end.
| Parameter | Description |
|---|---|
q | the text entered; if it is empty and there is no step-by-step parameter, the list is empty too |
limit | at most this many suggestions, from 1 to 20; default: 5 |
postal_code, settlement, street | step-by-step selection — details: Step-by-step selection |
Response: {"query": …, "suggestions": [address object, …]}. If there is no match, the list is empty; this is not an error, the response code is 200.
Send parameter values URL-encoded: curl does this with the --data-urlencode option, the requests library (Python) with the params argument.
While the user is typing, do not send a request on every keystroke: wait until there is a short pause in the typing. Our demo, for example, sends the request 180 milliseconds after the last keystroke. This way you send fewer requests and do not hit the per-minute limit needlessly.
curl -sG https://adatka.hu/suggest -H "Authorization: Bearer $ADATKA_KEY" --data-urlencode "q=9022 gyor baross gabor ut 12" --data-urlencode "limit=1"Expected result 9021 Győr, Baross Gábor út 12, and the value of meta.postal_not_matched is "9022": the postal code entered does not belong to this address.
Step-by-step selection
If your form asks for the address step by step — postal code or settlement, street, house number — send the part already selected in a separate parameter, and in the q parameter only what the user types after it. Take the values from the fields of the selected suggestion.
| Parameters | What it returns |
|---|---|
postal_code=6720 | the settlements belonging to the postal code |
settlement=Szeged, q=Tisza | those streets of Szeged that match the text “Tisza” |
settlement=Szeged, street=Tisza Lajos körút, q=2 | those house numbers of the street that match the text “2” |
settlement=Budapest, settlement_part=Békásmegyer | the streets of Budapest, with those verifiably located in the settlement part first (for these, settlement_part is filled in), followed — when q is empty — by those closest to the centre of the part |
postal_code can also be given together with settlement: it then narrows the matches. If the user has chosen a settlement part (the settlement_part field of the suggestion), send it in the settlement_part parameter alongside settlement; the same applies to house numbers: for a house verifiably located in the part, settlement_part is filled in; for a house outside the boundary of the part, the settlement_part_not_matched flag appears.
curl -sG https://adatka.hu/suggest -H "Authorization: Bearer $ADATKA_KEY" --data-urlencode "settlement=Szeged" --data-urlencode "street=Tisza Lajos körút" --data-urlencode "q=2"Expected result five house numbers (2, 20, 21, 22 and 23), each with "match_level": "house".
Address from coordinates (reverse geocoding) — GET /reverse
The known addresses with a house number closest to the point, the closest first. (Coordinates from an address are given by /suggest and /clean: in the geo field of the address object; their precision is shown by geo.precision.)
| Parameter | Description |
|---|---|
lat, lon | required; latitude and longitude in decimal degrees (WGS84), with a decimal point, for example 46.2557 and 20.1562 |
limit | at most this many addresses, from 1 to 20; default: 1 |
Response: {"lat": …, "lon": …, "suggestions": [address object, …]}.
curl -sG https://adatka.hu/reverse -H "Authorization: Bearer $ADATKA_KEY" --data-urlencode "lat=46.2557" --data-urlencode "lon=20.1562"Expected result 6720 Szeged, Tisza Lajos körút 2 ("match_level": "house").
The response always gives the nearest known house in Hungary, but not the distance. If the point is far from houses — in a field, on water or beyond the national border — the address found may be far from the point; if this matters, calculate the distance from the geo.lat and geo.lon fields. If limit is greater than 1, the same address may appear more than once, because in the source one house may have several address points; filter out the repetitions before displaying them.
Address list validation — POST /clean
One address per row, at most 1,000 rows per request. In the response each row has exactly one address object, in the same order; the original row is in the input field.
The request body is JSON (Content-Type: application/json): {"records": ["…", "…"]}. Response: {"results": [address object, …]}.
curl -s https://adatka.hu/clean -H "Authorization: Bearer $ADATKA_KEY" -H "Content-Type: application/json" -d '{"records": ["Szeged, Tisza Lajos krt. 2", "pecs kiraly u 999"]}'Expected result two address objects in the results list; the first is 6720 Szeged, Tisza Lajos körút 2 ("quality_code": 0), the second Pécs, Király utca ("quality_code": 1, "house_not_found": "999").
quality_code | Meaning | What to do |
|---|---|---|
| 0 | house-level match (house), confidence at least 0.8 | can be used |
| 1 | the match is incomplete or uncertain: street or settlement level, the house number is only known to exist (house_exists), or confidence is below 0.8 | look at the flags in meta and check if necessary |
| 2 | we did not find an address | check manually |
| 3 | the row contains no address: it is empty, or contains neither letters nor digits | — |
If you send more than 1,000 rows, the response is 413: split the list into parts of at most 1,000 rows — see the Python example.
Status — GET /health
For monitoring: whether the service is running, and with which dataset. No key is needed, and it does not count towards the limits.
curl -s https://adatka.hu/healthExpected result JSON containing "message": "adatka él!" (Hungarian for “adatka is alive!”) and (during the beta) "api_key_required": false.
| Field | Meaning |
|---|---|
message | "adatka él!" if the service is running |
api_key_required | true if the key is mandatory; currently false during the beta |
reference_data | the identifier of the dataset (run_id) and the time it went live (promoted_at_utc) |
The other fields are internal operational data and not part of the documented interface; your program should not rely on them.
Address object
/suggest, /reverse and /clean return an object with the same structure (/clean also adds the input field). If a piece of data is not known, the field value is null. A real address object — the first suggestion in the /suggest response for the text “Szeged Tisza Lajos körút 2”:
{
"country_code": "HU",
"postal_code": "6720",
"settlement": "Szeged",
"settlement_part": null,
"street": "Tisza Lajos körút",
"house_number": "2",
"sub_premise": {
"building": null,
"staircase": null,
"floor": null,
"door": null,
"verified": false
},
"admin": {
"county": "Csongrád-Csanád",
"county_ksh": null,
"district": "Szegedi",
"district_ksh": "075 1",
"city_district": null,
"settlement_ksh": "33367",
"iso_3166_2": null,
"geoname_id": "715429"
},
"geo": {
"lat": 46.255697,
"lon": 20.156243,
"precision": "house",
"timezone": "Europe/Budapest"
},
"meta": {
"match_level": "house",
"confidence": 0.99,
"quality_code": 0,
"sources": [
"OSM",
"KSH",
"GeoNames"
]
}
}| Field | Meaning |
|---|---|
country_code | country code; currently always "HU" |
postal_code | postal code. If several postal codes belong to the address and the house number does not decide which one applies: for a settlement-level match, the lowest and the highest ("6710 ... 6791"); for a street-level match, null |
settlement | the official name of the settlement |
street | the name of the street together with its type ("Tisza Lajos körút") |
house_number | house number as it appears in the source ("2/1", "13-15") |
sub_premise | building, staircase, floor, door (building, staircase, floor, door), if the text entered contains them; verified is always false, because in the absence of a public source we do not check these |
admin.county | county (vármegye) |
admin.district, admin.district_ksh | district (járás) and its KSH code |
admin.city_district | the city district (kerület) of Budapest ("XIII. kerület") |
admin.settlement_ksh | the KSH code of the settlement |
admin.geoname_id | the GeoNames ID of the settlement |
geo.lat, geo.lon | coordinates in decimal degrees (WGS84) |
geo.precision | precision of the coordinates: house, street, settlement_part or settlement |
geo.timezone | time zone ("Europe/Budapest") |
meta | the level, reliability, sources and flags of the match — details: Match levels and flags |
settlement_part is the settlement part: a city quarter, housing estate or other built-up area (for example Békásmegyer, Jereván-lakótelep, Felsőváros). A settlement part can also be a match in its own right ("match_level": "settlement_part"). For an address match, it gets a value if the request names the settlement part and the address found is verifiably in that part: it lies within the boundary of the part (where the boundary is known — in Budapest, for most city quarters), or it has a postal code that, within the settlement, belongs only to this part, or — for a house with its own coordinates — of all parts of the settlement, the centre of this part is the closest to it, at most 1 km away. If we cannot verify that the address belongs to the part, the field stays empty. Two fields are part of the schema but are currently not filled in: admin.county_ksh, admin.iso_3166_2. For a Budapest address, admin.county and admin.district are null, because the capital belongs to neither a county nor a district (járás); the city district (kerület) is given by admin.city_district.
Match levels and flags
meta tells you what we found and how certain the match is:
| Field | Meaning |
|---|---|
match_level | the match level — see the table below |
confidence | the reliability of the match, a number between 0 and 1 |
quality_code | 0–3; its meaning is given in the description of /clean. It may also appear in /suggest and /reverse responses, but there rely on the value of match_level. |
sources | the sources used: "KSH", "GeoNames", "Magyar Posta", "Nemzeti Választási Iroda" and OpenStreetMap — named "OSM" for a house-level match and "OpenStreetMap" for a street-level match; details: Sources and licences |
match_level | Meaning |
|---|---|
house | we found the house number; the coordinates are those of the house |
house_exists | the house number appears in the address dataset of the National Election Office (Nemzeti Választási Iroda), but we have no coordinates of our own for it; the precision of the coordinates is shown by geo.precision. In this case the meta.house_exists flag also appears. |
street | we found the street but not the house number — or the request contained no house number |
settlement_part | we found the settlement part (city quarter, housing estate, other built-up area): the response contains the name of the part (settlement_part), the settlement, the postal code or postal code range belonging to the part, and the centre of the part; in Budapest also the district (admin.city_district) |
settlement | we found only the settlement |
none | no match (only in the /clean response) |
If part of the text entered does not fit the address found, the response does not hide this: meta flags it in a separate field. The value of the flag is the part of the text entered (the city district in standardised form); the exception is house_exists, whose value is an object.
| Flag | When it appears | Example: entered → response |
|---|---|---|
postal_not_matched | the postal code entered does not belong to this address; the postal_code field contains the correct one | “9022 gyor baross gabor ut 12” → 9021, "9022" |
postal_alias | another source assigns the postal code entered to the settlement; the postal_code field contains the one according to Magyar Posta | “csömend 8700” → 8703, "8700" |
house_not_found | the house number entered is not in our data; the response is at street level | “pecs kiraly u 999” → Király utca, "999" |
house_exists | the house number appears in the address dataset of the National Election Office; the value is an object: source, dataset, extract_date (the date of the extract), street_matching (who matched the street name) | “tiszafured petofi u 12” → Petőfi Sándor utca 12 |
street_not_found | we did not find the street entered; the response is at settlement level | “szeged nincsilyen utca 5” → Szeged, "nincsilyen utca" |
settlement_not_matched | the settlement name entered does not fit the address found | “vaci ut 1 godol” → Budapest, Váci út 1, "godol" |
kerulet_not_matched | the city district (kerület) entered does not fit the address found | “1136 hegedus gyula utca 4 xxi ker” → XIII. kerület, "XXI. kerület" |
settlement_part_not_matched | the address found is definitely not in the settlement part entered: the house lies outside the boundary of the part, or the address is in a different city district (kerület) | “bekasmegyer andrassy ut 1” → Andrássy út (VI. kerület), "bekasmegyer" |
The value of the house_exists flag for the request “tiszafured petofi u 12”:
{
"source": "Nemzeti Választási Iroda",
"dataset": "Körzetállomány (szavazóköri címállomány)",
"extract_date": "2026-01-16",
"street_matching": "Adatka"
}A missing match is not proof: if a house number does not appear in Adatka’s data sources, that does not mean the address does not exist. A street- or settlement-level response, or an empty one, should not by itself prevent an order, a registration or the handling of an administrative matter — your program should always offer a way to enter or check the address manually (Terms of service, in Hungarian).
New flags may also appear: your program should ignore what it does not recognise.
Error codes
For a rejected request, the response body is: {"detail": {"error": "…", "message": "…"}}. message is in Hungarian and meant for people; in your program, rely on the value of error. The bodies of 413 and 422 responses have a different form.
| HTTP code | error | When | What to do |
|---|---|---|---|
| 401 | missing_api_key | there is no key in the request, and the key is mandatory | send the key in a header |
| 401 | invalid_api_key | the key is unknown or revoked — also during the beta | check the key |
| 403 | server_key_in_browser | a server key arrived from a browser (with an Origin header) | a website needs a browser key |
| 403 | endpoint_not_allowed | the /reverse or /clean endpoint was called with a browser key | these need a server key |
| 403 | origin_not_allowed | the request sent with a browser key came from a website that is not assigned to the key, or it has neither an Origin nor a Referer header | if the website is not assigned to the key, send us its web address; if the request does not come from a browser, use a server key |
| 413 | — | a /clean request contains more than 1,000 rows; the body: {"detail": "Too many records: max 1000"} | split the list |
| 422 | — | invalid parameter or body, for example: limit is not between 1 and 20, lat or lon is missing, records is missing; the detail list in the body tells you which field is wrong (loc, msg) | correct the request |
| 429 | rate_limited | too many requests: you have reached the limit without a key, the per-minute limit or (for a browser key) the daily limit — see Limits. Without a key, the message field of the body tells you which limit applies and how you can request a key; if a /clean request contains more rows than can be sent in one request without a key, the max_rows field of the body gives the maximum number of rows | wait as many seconds as the Retry-After header (and the retry_after field of the body) specifies; do not retry more often than the header asks |
| 5xx | — | an error on our side | repeat the request later, with increasingly long waits; if the error persists, write to info@adatka.hu |
Limits
/suggest,/reverse: at most 20 addresses in one response (limit)./clean: at most 1,000 rows in one request.- Server key: by default 120 requests per minute.
- Browser key: by default 120 requests per minute and 2,000 per day, both per visitor (per IP address).
- Without a key, until 4 November 2026 — an anti-abuse brake (Terms of service, section 5, in Hungarian): a reserve of 60 requests per IP address, refilled at 10 requests per second. Each row of
/cleancounts as one request, so without a key at most 60 rows can be sent in one request. - Without a key, from 5 November 2026 — quota: a reserve of 60 requests per IP address, refilled at 2 requests per second (about 120 per minute), and at most 3,000 requests per day; in
/clean, at most 60 rows in one request. For IPv6, a /64 network counts as one address. - For all requests without a key together: 30 requests per second (reserve of 60). If this runs out, requests without a key receive 429; requests sent with a key are not affected — a key gives priority.
- Limits for keys are counted per whole minute and calendar day, not with a sliding window: the counter resets at every full minute and at midnight UTC respectively. The reserve without a key refills continuously at the stated rate; the daily quota without a key resets at midnight UTC. The
Retry-Afterheader of a 429 response tells you after how many seconds you can try again.
We state the limits for your key in the email in which we issue the key; if they differ from the above, the email prevails. The limits without a key are there to protect the service: one IP address should not be able to take capacity away from the others (Terms of service, section 5, in Hungarian). If you regularly hit the limit without a key, request an API key in the “Access” section: a key comes with a higher quota and priority. The limits without a key do not apply to operational requests sent from the server’s own machine.
Python example
A complete example with the requests library: it takes the key from the ADATKA_KEY environment variable, after a 429 response waits as long as Retry-After asks, and sends a long list in parts of 1,000 rows. Run it in the same terminal in which you entered the key.
Install the requests library into a virtual environment of your own — newer Linux systems do not allow pip install to install into the system Python:
python3 -m venv adatka-venv && . adatka-venv/bin/activate && pip install requestsIf python3 -m venv gives an error (“ensurepip is not available”), on Debian and Ubuntu first install the python3-venv package. In a new terminal, activate the environment again (. adatka-venv/bin/activate) and enter the key again.
Save the example as adatka_pelda.py:
import os, time
import requests # pip install requests
API = "https://adatka.hu"
session = requests.Session()
session.headers["Authorization"] = "Bearer " + os.environ.get("ADATKA_KEY", "") # in the beta it also runs without a key
def call(method, path, **kw):
for attempt in range(3):
r = session.request(method, API + path, timeout=30, **kw)
if r.status_code == 429: # rate limit: wait as long as the response asks
time.sleep(int(r.headers.get("Retry-After", "1")))
continue
r.raise_for_status()
return r.json()
raise RuntimeError("Adatka: still rate-limited after three attempts")
def clean_all(lines): # at most 1000 rows per request
results = []
for i in range(0, len(lines), 1000):
results += call("POST", "/clean", json={"records": lines[i:i + 1000]})["results"]
return results
hints = call("GET", "/suggest", params={"q": "Szeged Tisza Lajos körút 2", "limit": 5})["suggestions"]
rows = clean_all(["Szeged, Tisza Lajos krt. 2", "pecs kiraly u 999"])
print(hints[0]["postal_code"], [r["meta"]["quality_code"] for r in rows])python3 adatka_pelda.pyExpected result 6720 [0, 1] — the postal code of the first suggestion and the quality_code values of the two rows.
In other programming languages it works the same way: the key in the header, the parameters URL-encoded, the response in JSON.
Data protection in requests
Send only addresses or coordinates. Do not send names, email addresses, phone numbers, or client or order identifiers (Terms of service, section 5, in Hungarian); if the address field of an order also contains the recipient’s name or phone number, remove it before sending.
We do not log the text of requests. From GET /suggest and GET /reverse requests, only the path, the parameter names and the length of their values, and the response code go into the system log, without IP address or API key; we keep these entries for at most 14 days. The body of POST /clean is not logged. Details: Privacy notice.
Changes
6 October 2026: step-by-step selection with a settlement part — new settlement_part parameter alongside settlement (Step-by-step selection).
6 October 2026: for an address match, settlement_part gets a value only if membership of the part can be verified — from the boundary of the part, a postal code belonging only to the part, or the nearest part (Address object); the settlement_part_not_matched flag also appears when the house lies outside the boundary of the named part (Match levels and flags).
5 October 2026: settlement parts in search — city quarter, housing estate, other built-up area (“Békásmegyer”, “Jereván-lakótelep Sopron”, “szeged felsőváros”): new match level ("settlement_part"), the settlement_part field filled in for an address found within the postal codes of the part, new flag (settlement_part_not_matched); source: OpenStreetMap and KSH Gazetteer (Helységnévtár) (Match levels and flags, Sources and licences).
5 October 2026: limits without a key — an anti-abuse brake with immediate effect, a numerical quota from 5 November 2026; without a key, the body of the 429 response states the reason (Limits, Error codes).
5 October 2026: the documentation now describes how to call the API from a browser (CORS) (API key) and the rule for handling a missing match (Match levels and flags).
A new field, flag or endpoint may appear at any time; your program should ignore what it does not recognise. We will inform you of material changes to existing fields and endpoints in accordance with section 8 of the Terms of service (in Hungarian).
Access
The service is currently available as an invitation-only beta, free of charge; there is no self-service registration yet. Request access, and we will get in touch with you.