The same engine,
inside your app.
One endpoint per service, one call, the response documented field by field: contact verification, deduplication, email, phone, website, Italian tax code, autocomplete. Single or in batches; for large files the job is queued and you collect it when it is ready.
OpenAPI specification: openapi.json, to generate the client or import it into your tool.
One endpoint per service, with the same name the service has on this site: /contact, /dedupe, /email, /phone, /website, /tax-code, /enrichment. Each accepts one item in the request body or a list in items, up to 500 per call (100 for websites, which have to be contacted one by one). Deduplication is the exception and always wants a list: it compares the records with each other. Apart stand /suggest, the autocomplete for forms (it works per session while the user types, not in batches), and /credit, which says how many operations are left and the state of each counter, without using any.
Authentication
The API host is www.radaraddress.com. Field names, endpoint names and values are English identifiers, the same in every language; labels, reasons and messages follow the language of the request ("language": "de" or ?language=de, or the Accept-Language header, or the language of the account). The Content-Language header says which language the response came in.
Access to the API is by Bearer token. The account is free: you create the token in your account area and include it in the header of every request in this form:
# Every request needs the Authorization header Authorization: Bearer {your-token}
When creating it you can give the token an optional expiry: past that date requests receive HTTP 401 with code token_expired; without an expiry the token is valid until you revoke it. You can revoke or regenerate it at any time from your account area, where you also see the last use and the number of requests served. A request without a token or with an invalid token returns HTTP 401.
The Italian names, for those already using them
The API has one version, in English. Whoever integrated with the Italian names does not need to change anything: Italian fields are accepted in every request, the endpoints also answer under their Italian names (/contatto, /deduplica, /codicefiscale, /arricchimento, /telefono, /sito, /suggerisci, /nazioni, /credito), and the response comes out with the Italian keys and codes for whoever asks with "language": "it" or calls www.radaraddress.it without stating a language.
The full table of fields: English → Italian
| English | Italian | English | Italian |
|---|---|---|---|
account | account |
members | membri |
action | azione |
method | metodo |
activated_on | attivato_il |
metric | metrico |
active | attiva |
min_level | livello_minimo |
active_packs | attivi |
missing | mancano |
address | indirizzo |
modified | modificati |
address_key | indirizzo_confronto |
month | mese |
addresses | indirizzi |
monthly_cap_eur | tetto_mese_eur |
after | dopo |
multiple_postcodes | multicap |
age | eta |
municipality | comune |
agreement | consenso |
municipality_code | comune_codice |
ambiguous_year | anno_ambiguo |
municipality_code_type | comune_codice_tipo |
area | area |
n_records | n_anagrafiche |
async | asincrono |
name_id | nome_id |
auto_topup | auto_ricarica |
name_original | nome_originale |
available | disponibile |
national_number | nazionale |
before | prima |
nearby | vicino |
belfiore_code | codice_belfiore |
needed | necessarie |
birth_country | nazione_nascita |
needs_topup | da_ricaricare |
birth_date | data_nascita |
network | rete |
birth_place | comune_nascita |
normalized | normalizzato |
birth_place_name | luogo_nascita |
normalized_postal | normalizzato_postale |
birth_province | provincia_nascita |
not_found | non_trovati |
born_abroad | nato_estero |
note | nota |
bought | comprati |
notes | note |
building | edificio |
number | numero |
cadastral_code | catastale |
numbers | numeri |
canonical_address | indirizzo_canonico |
occurrences | occorrenze |
canonical_city | localita_canonica |
operations | elaborazioni |
care_of | presso |
operator | operatore |
certificate_ok | certificato_ok |
origin | origine |
changed | modificato |
other | altro |
changes | modifiche |
outcome | esito |
charged | consumate |
outcome_label | esito_label |
check | controlla |
outcomes | esiti |
check_digit | controllo |
overall_cap_eur | tetto_globale_eur |
city | localita |
packs | pacchetti |
city_key | localita_confronto |
parsed | letto |
city_passes | cicli_localita |
phase | fase |
city_type | localita_tipo |
phone | telefono |
class | classe |
phone2 | telefono2 |
code | codice |
phone3 | telefono3 |
colour | colore |
phones | telefoni |
comments | commenti |
place | luogo |
conditions | condizioni |
position | posizione |
confidence | confidenza |
postcode | cap |
confirmed | confermato |
postcode_check | cap_verifica |
consolidate | consolida |
precision | precisione |
contact | contatto |
preserve_original | preserva_originale |
contact_person | referente |
presumed | presunto |
corrected_from | corretta_da |
processed | lavorato |
counter | contatore |
processed_at | datalav |
counters | contatori |
profession | qualifica |
countries | nazioni |
provenance | provenienza |
country | nazione |
province | provincia |
country_code | nazione_iso2 |
provincial_capital | capoluogo |
country_original | nazione_originale |
reachable | raggiungibile |
country_prefix | prefisso_paese |
reason | motivo |
created | creato |
record_outcome | esito_record |
credit | credito |
records | anagrafiche |
dedupe | deduplica |
redirects | redirect |
detail | dettaglio |
reference | riferimento |
differences | differenze |
reference_id | riferimento_id |
discarded | scartati |
remaining | rimaste |
disposable | usa_e_getta |
renews_on | si_rinnova_il |
district | quartiere |
reset_on | si_azzerano_il |
domain_checked | dominio_verificato |
rows | righe |
domain_exists | dominio_esiste |
salutation | saluto |
done | fatte |
save | salva |
duration_ms | durata_ms |
segment_passes | cicli_arcostradale |
e164 | formato_e164 |
session | sessione |
education | titolo_studio |
short | breve |
entries | schede |
singles | singoli |
error | errore |
size | taglia |
exists | esiste |
source | fonte |
expiry | scadenza |
specificity | specificita |
explanations | spiegazioni |
spent_month_eur | speso_mese_eur |
extension | estensione |
spent_overall_month_eur | speso_globale_mese_eur |
field | campo |
state | stato |
final_url | url_finale |
states | stati |
first_name | nome |
street | strada |
foreign | esteri |
street_id | via_id |
foreign_address | estero |
street_name | toponimo |
formal_salutation | saluto_formale |
street_passes | cicli_toponimo |
format | formato |
street_proper_name | duf |
found | trovato |
street_type | dug |
free | gratuite |
sub_address | subindirizzo |
free_forever | gratis_per_sempre |
sub_address_confirmed | subindirizzo_confermato |
full_name | nominativo |
subject_type | tipo_soggetto |
gender | sesso |
subscription | abbonamento |
gender_source | fonte_sesso |
suffix | esponente |
generic | generica |
suggestion | suggerimento |
group | gruppo |
summary | riepilogo |
groups | gruppi |
supported | supportato |
hamlet | frazione |
syntax | sintassi |
hints | suggerimenti |
tax_code | codice_fiscale |
homocode | omocodo |
tax_code_outcome | codice_fiscale_esito |
house_number | civico |
territory | territorio |
house_number_label | civico_label |
text | testo |
house_number_verified | civico_verificato |
time_ms | tempo_ms |
house_numbers | civici |
title | titolo |
informal_salutation | saluto_informale |
to_check | da_controllare |
iso2 | codice_iso2 |
total | totali |
iso3 | codice_iso3 |
town | citta |
istat_code | istat |
truncated | troncato |
items | elementi |
type | tipo |
job | lavoro |
type_normalized | tipo_norm |
language | lingua |
types | tipi |
languages | lingue |
unresolved | non_risolti |
last_name | cognome |
url_normalized | url_normalizzato |
last_on | ultimo_il |
used | usate |
last_used | ultimo_uso |
valid | valida |
left | residuo |
value | valore |
legacy_default | default_storico |
warning | avviso |
level | livello |
warnings | avvisi |
light | leggere |
website | sito |
long | lungo |
website2 | sito2 |
long_name | nome_esteso |
website3 | sito3 |
main_record | principale |
websites | siti |
match | corrisponde |
zoned | zonato |
matches | abbinamenti |
Option values have an Italian name too: format nazionale/internazionale, action genera/valida/estrai/confronta/seleziona, foreign dichiarati/rileva, min_level certo/probabile/ambiguo; with "language": "it" so do level, confidence, state, the outcome codes and the file headers. Common synonyms are accepted on input too (zip, surname, phone_number, date_of_birth…), and the same headers in the files of batch jobs.
Contact verification endpoint
Fixes the whole contact: the address in the postal standard of its country — in Italy and in the countries whose register is in service, street and house number are verified in the national address register; elsewhere the address comes out in the country's postal form —, the name and — if the request carries it — the Italian tax code, which is cleaned up, completed when only the check character is missing and compared with surname, first name, sex and date of birth. One contact at a time, or up to 500 in items: the schema is the same as every other endpoint.
The country is given with country_code (ISO 3166-1: IT, DE, FR…) or with country, written any way: “Germania”, “Germany”, “Deutschland”, “Federal Republic of Germany”, “UK”, “Holland”. If missing, Italy is assumed. In the countries whose register is in service — today France, Germany, Spain, Netherlands, Belgium, Finland, Czechia, Portugal, Denmark, Norway, Austria, Switzerland, Slovakia, Croatia, Romania, Hungary, Slovenia, Ireland, Iceland, Luxembourg, Liechtenstein, San Marino, Monaco, Andorra, Vatican City, the up-to-date list is in Countries — street and house number are verified in the national address register just as in Italy: postcode confirmed or filled in, coordinates of the house number in geo, district or arrondissement in district where the city has them (Hamburg-Altstadt, Paris 4e Arrondissement), municipality code in territory.municipality_code. In Italian the outcome carries FOREIGN followed by the changes, in the other languages only the changes; if the street exists and the number does not, HOUSE_NUMBER_NOT_FOUND. In the other countries we work on the form, and the message says so: postcode in the country's format, street abbreviations expanded, capitals and city name as the post of that country writes them (Hauptstr. 5, Munich → Hauptstraße 5, MÜNCHEN), with the country line. In both cases address_key comes back, the form the deduplication uses to recognise the same street written in two ways. With "foreign": "detect" a record without a country that is not found in Italy is recognised as foreign when the text says so clearly; the default declared leaves as foreign only what declares it. The full catalogue of countries, with short and official name, in English and in the country's language, is available with GET /api/v1/countries and can be queried with ?q=Germania, ?q=Deutschland or ?q=DE.
curl -X POST https://www.radaraddress.com/api/v1/contact \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"address":"via leopardi 4","postcode":"20100","city":"milano","province":"mi","id":"RIF-001"}'
For the batch, the same fields inside items; the response is { count, results: [ { id, result } ] } in the same order as sent.
curl -X POST https://www.radaraddress.com/api/v1/contact \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"items":[{"id":"1","address":"via leopardi 4","city":"milano"},
{"id":"2","address":"via roma 1","postcode":"20121","tax_code":"RSSMRA85M01H501Q"}]}'
The same endpoint for an address in another country: country_code is enough. Here Hamburg, verified in the German register, with the district and the coordinates of the house number.
curl -X POST https://www.radaraddress.com/api/v1/contact \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"address":"Mönckebergstr. 7","postcode":"20095","city":"Hamburg","country_code":"DE"}'
→ { "outcome": "FOREIGN MODIFIED CITY_FORM STREET_FORM",
"normalized": { "address": "Mönckebergstraße 7", "postcode": "20095", "city": "HAMBURG", "country": "Germany", "country_code": "DE" },
"municipality": "Hamburg", "district": "Hamburg-Altstadt", "address_key": "MONCKEBERGSTRASSE",
"geo": { "lat": 53.5512437, "lon": 10.0028651, "precision": "house_number", "source": "inspire" },
"territory": { "country": "DE", "municipality_code": "AdminUnitName_49021011010101", "municipality_code_type": "inspire" } }
Options valid for the whole call: postal_form (forces normalized into the postal form too), preserve_original (keeps the name as the user wrote it and exposes the canonical one separately), precision 1–5 (default 3; above 3 the match is approximate and the reliability will not be high).
Together with the address comes city_type, which says whether the delivery point is in a provincial capital or in a town of the province — the difference that matters to whoever segments a campaign by city. provincial_capital is true, false or null when we have no elements to say — and in that case we don't guess.
→ { "city_type": { "provincial_capital": true, "label": "Provincial capital" } }
Next to the postcode comes postcode_check: where it comes from (source) and how precisely (precision: house_number the number itself, interpolated the neighbouring numbers of the same street, street the majority of the street, locality, municipality), how many house numbers say so (house_numbers) and with what agreement (agreement, from 0 to 1). When there is no confirmation for that number the outcome carries POSTCODE_UNCONFIRMED and confirmed is false: the postcode stays as given, if it is one of the city's, and should be checked.
→ { "postcode_check": { "source": "osm", "precision": "house_number", "house_numbers": 3, "agreement": 1, "confirmed": true } }
The coordinates come back too, in geo (WGS84 latitude and longitude), and the territorial identifiers in territory: in Italy the ISTAT code and cadastral code of the municipality, CAB and the national identifier of the street; in the other countries country and the code of the municipality in the national register (municipality_code). The precision field says what level we reached: house_number when the house number is georeferenced, interpolated when the exact number is missing and the point is estimated, street when we have the point of the street, municipality when we only have the centre of the municipality. source says where the point comes from: anncsu is the Italian national register, inspire the national register of the country, osm OpenStreetMap: in that case the data are © OpenStreetMap contributors, ODbL licence, and the attribution must be reproduced if you publish them. Where a country's register requires an attribution, it comes out in the message. In the CSV of batch jobs they are the columns latitude, longitude, geo_precision, istat_code and cadastral_code.
→ { "geo": { "lat": 45.4762711, "lon": 9.207104, "precision": "house_number", "source": "anncsu" },
"territory": { "istat_code": "015146", "cadastral_code": "F205", "cab": "01600", "street_id": "1026700" } }
Floor, staircase and flat written in the address also come back as data of their own in sub_address: a list of type and value pairs, read with the postal rules of the country (in Germany after “//”, in France on a line of its own, in Portugal “3º Esq”). The types are unit (flat), staircase, floor, block, building, building_number and block_number. The address line stays in the country's form. What we do not recognise stays as written and is not listed: we do not guess it. When the unit written is on record at that house number, sub_address_confirmed is true; otherwise it is null, never false: not finding it does not mean it is wrong.
→ { "sub_address": [ { "type": "floor", "value": "3" }, { "type": "unit", "value": "ESQ" } ], "sub_address_confirmed": true }
Deduplication endpoint
Recognises the records referring to the same person at the same address even when they are written differently: nicknames and name equivalences (Dany ≈ Daniela), surname and first name swapped, abbreviations ("V. Roma" ≈ "Via Roma"), typos. The comparison of addresses goes through the normalisation engine: two spellings of the same street collapse onto the canonical form before the comparison. At most 500 contacts per call (records + reference); for larger lists use the batch job from your account area.
Address books. A contact in an address book (Apple Contacts, Google, Outlook: the vCard model) has several addresses, phones and emails, each with a label. The endpoint accepts them as they are, with no cap: addresses is a list of objects with type and the usual fields; phones and emails are lists where each item is the bare value ("340 7491386") or {"type": "work", "value": "02 66710423"}, mixed too; the usual flat fields remain valid and count as the first address and first contact detail. Two contacts are the same person if any pair of their addresses matches (the office of one with the only address of the other), or if they have the same name and a phone or an email in common, in any position and with any label. The label doesn't weigh on the match: it comes back as it arrived, plus type_normalized in the vCard vocabulary (home, work, cell…), so the app knows where to write back. One contact is one operation, however many addresses and contact details it has.
curl -X POST https://www.radaraddress.com/api/v1/dedupe \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "records": [ { "id": "C1", "last_name": "Rossi", "first_name": "Daniela", "address": "Via Leopardi 4", "postcode": "20123", "city": "Milano", "province": "MI" }, { "id": "C2", "last_name": "Rossi", "first_name": "Daniela", "address": "V. Leopardi 4", "postcode": "20100", "city": "milano", "province": "MI" } ] }'
The response groups the duplicates in groups: each group lists the ids of its members, says which one to keep (main_record) and proposes the consolidated record: the best name and the union of addresses and contact details (addresses, phones, emails), each with its type, with provenance (which contacts it comes from) and deduplicated: the same street in two spellings is one address, the same number with two labels one number. Contacts with no match are in singles, as objects {id, outcome}: with outcome INTERNAL_DUPLICATE the contact has no duplicates with others but has some inside itself (address written twice, repeated number), and carries its merged record. It recognises the same street written in different ways ("V. Leopardi 4" ≈ "Via Giacomo Leopardi 4"), the postcode fixed automatically and surname and first name swapped.
| Parameter | Type | Description |
|---|---|---|
records | array | Required. Records with last_name, first_name, address, postcode, city, province and optional id, gender, email, phone, country. Foreign records (field country, or province EE) are recognised even when written differently (“Hauptstr. 5” and “Hauptstraße 5”); two different countries are never the same record. A phone or an email in common brings two records together even with a different address: at most probable if the contact detail is personal, only ambiguous if it belongs to a shared place (a landline, a generic email) |
addresses, phones, emails | array | Optional, inside each record: the address-book lists, with no cap (see above). phone and email accept the same three forms: the bare value, a list of values, a list of {type, value} |
tax_code | string | Optional, inside each record. If it is valid and consistent with the record's name, two records with the same code are the same person even at different addresses (certain, reason “same tax code”); with two valid, different codes they are never certain nor probable. A code that doesn't match the name carries no weight |
reference | array | Optional: two-list mode. Each record is searched in the reference; response with matches and not_found |
min_level | string | certain | probable (default) | ambiguous: how elastic the match is. certain = street and house number must coincide; ambiguous ignores the house number. Two records with neither address nor city are never certain |
foreign | string | declared (default: foreign is only the record that gives the country) | detect (also from explicit signals in the text: name of the country, known foreign city, postcode in a non-Italian form) |
save | bool | Default true: the result stays readable for 30 days with GET /api/v1/dedupe?job=<code>; the code arrives in the job field. With POST {"job", "group", "processed": true} you mark a group as reviewed |
Italian tax code endpoint
Three actions on the Italian tax code of natural persons: generate from the personal data, validate an existing code (format, check character and omocodia), extract the information it contains — date of birth, age, sex, municipality or foreign country of birth. In addition compare checks that a code matches the declared personal data. It covers the cadastral codes of every Italian municipality and of foreign countries. Surname and first name must be passed in Latin characters: for someone whose name is in another alphabet, the code is computed on the transliteration shown on the document, which is not unique (Dmitrij/Dmitry); a non-Latin name returns cognome_non_latino or nome_non_latino, and in compare a difference in the name of someone born abroad carries a note about a possibly different transliteration.
The first 500 light operations a month are free (tax code included), and the allowance is a single one across every way of using the service: what you do here, on the website and in batch files counts on the same allowance. Beyond it, light operations are paid at their price (see pricing).
curl -X POST https://www.radaraddress.com/api/v1/tax-code \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "action": "generate", "last_name": "Rossi", "first_name": "Mario", "gender": "M", "birth_date": "1980-01-01", "birth_place": "Roma" }' → { "ok": true, "tax_code": "RSSMRA80A01H501U", "place": "Roma", "province": "RM" }
For volumes: { "action": "validate", "items": [ … ] } processes up to 500 items per call, each with its own correlation id. Extraction flags with ambiguous_year the cases where the two digits of the year don't tell the century apart (1926 vs 2026).
Every response carries outcome, reason, notes and comments in the language of the request: GENERATED for generate, empty for a valid code in validate, MATCH or MISMATCH for compare (with differences: the fields that do not match). When the code fails the checks the outcome is one of the TAX_CODE_* codes listed below and error gives its short form (check_digit, length, format, homocode, month, date, place, empty); on generate it says which datum is missing or unresolved (last_name, first_name, gender, birth_date, birth_place, ambiguous_place with options, last_name_non_latin). suggestion carries the correct code when the check can rebuild it.
Enrichment endpoint
Enriches a name: sex inferred from the first name, type of subject (natural or legal person), normalised title (Dott.ssa, Avv., …) and salutations ready for correspondence — "Egregio Sig. Rossi", "Gentile Dott.ssa Bianchi", "Spett.le" for companies, "Gentile Famiglia" for households, "Caro/Cara" for the informal register. It also recognises the married form in the surname ("Rossi in Verdi" → woman, with the detail of the two surnames). The gender field also accepts written forms (“maschio”, “donna”, “Sig.ra”, “male”); X marks an organisation and G a family or a couple. If the title is missing, we derive it from profession (the profession) or from education, when one is given. Dictionaries are Italian.
curl -X POST https://www.radaraddress.com/api/v1/enrichment \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "last_name": "Rossi", "first_name": "Dott.ssa Maria" }' → { "ok": true, "gender": "F", "subject_type": "natural_person", "title": "Dott.ssa", "formal_salutation": "Gentile Dott.ssa Rossi", "informal_salutation": "Cara Maria", … }
In batches: { "items": [ … ] }, up to 500 per call. The outcome is ENRICHED (gender and salutations set), LEGAL_PERSON (company or organisation: treated as a company name) or PARTIAL (first name missing, or gender not inferable): reason says why, comments what to add to complete it.
Email verification endpoint
Verifies an email address: syntax, existence of the domain (MX/A records), typos of common domains with a proposed correction (gmial.com → gmail.com), disposable domains, role-based addresses (info@, accounts@: not a person). We don't do SMTP verification of the single mailbox, an intrusive and unreliable practice. Batch items up to 500; "dns": false skips the domain lookup.
A certain typo is corrected outright: when the address as written is not even an address (name@domain,com with a comma) or when the correction lands on a well-known provider (gmai.com → gmail.com, also one letter away when the domain as written does not receive mail), email comes out corrected, corrected_from carries what was written and the outcome is MODIFIED EMAIL_DOMAIN. A possible typo on any other domain (rossi.con) remains a proposal: EMAIL_TYPO with the correction in suggestion, and the checks (domain_exists, domain_checked) run on it; the address as written is not verified.
Phone verification endpoint
Checks a number without calling anyone: form, class and type, length according to the numbering plan, district of the landline and operator the block was originally assigned to. The class is the reading you need to work a list: mobile (you can text it), landline (you call it in office hours), special (not a person's number: emergency, public utility, toll-free and premium-rate numbers), foreign for numbers with an international prefix. When we also recognise the precise service, type says so (toll-free, premium rate, shared cost, public utility). The Italian prefix written without the + (39347…, a classic export typo) is removed when the digits leave no doubt — 3934567890 stays the mobile it is. If the same field holds several numbers (“347… - 338…”) we split them: they come back as phone, phone2, phone3, and phone is never empty when at least one number is there. With "format": "international" the Italian number comes out as +39…; the default national leaves it bare and adds the prefix only to foreign numbers. For foreign numbers you also get e164, the country of the prefix in country_code, and the trunk zero after the prefix is removed (“+44 (0)20…” and “+44 20…” give the same number). With country_code (or country) in the call or in the item, a number written without a prefix is read as a national number of that country: “020 7946 0958” in a UK record is London, not Milan. Without a country it stays Italian. Batch items up to 500.
A number from the record's own country is not "foreign": where we know the numbering plan it gets its class (mobile, landline, special), with format national it stays without country code in the form of its country (trunk zero included), and national_number is returned next to e164. PHONE_FOREIGN and class foreign remain for numbers of a country other than the record's.
curl -X POST https://www.radaraddress.com/api/v1/phone \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"phone":"3470328959 - 011 1253265"}'
Website verification endpoint
Checks that the address is well formed and that the site really responds: existence of the domain, HTTP request with redirects followed, final code, validity of the certificate. No judgement on the content. Here too several addresses in the same field come back as website, website2, website3. With "network": false we check only the form, without contacting the site. Batch items up to 100: every check opens a connection, so the batch is smaller than on the other endpoints.
curl -X POST https://www.radaraddress.com/api/v1/website \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"website":"www.example.com"}'
Address autocomplete endpoint
Starting from scratch? The widget ra-suggerisci.js and a small proxy on your server are all it takes: the token never goes to the browser.
Suggestions while the user types an address in your form: the whole Italian street register, with the name completed (“via verdi” → “Via Giuseppe Verdi”), municipality and province; the postcode comes with the selection, in the big zoned cities the right one for the house number. It works per session: the client generates a UUID for every address being filled in, the suggestion queries are free, and you pay one contact verification when the user selects and the fields are filled (action select, which returns the record already normalised by the engine). The fields already filled in the form — even partially — travel as context and narrow the suggestions.
curl -X POST https://www.radaraddress.com/api/v1/suggest \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"session":"CLIENT-UUID","q":"via verdi","city":"monz"}'
# when the user picks one: closes the session and charges the selection
curl -X POST https://www.radaraddress.com/api/v1/suggest \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"session":"CLIENT-UUID","action":"select","street_id":1063428,"house_number":"4"}'
Once the street is chosen, the house number completes: passing street_id with the partial number you get the existing numbers of that street, with exists true/false to validate the one typed — and the selection can be repeated with the house number at no extra charge: the charge is per session and per street, not per click; another street in the same session is another charge. House numbers are completed only in the session that selected that street. In zoned cities it is the house number that determines the exact postcode.
The API token never goes into the browser: the ready-made widget (ra-suggerisci.js) calls a small proxy on your server, which adds the token and forwards. Each session allows up to 30 calls and lasts 10 minutes; sessions without a selection are free up to 200 a day per token, plus five for every selection. Widget, ready proxy and example form are in the kit of Your own form.
Works in every country in service (today France, Germany, Spain, Netherlands, Belgium, Finland, Czechia, Portugal, Denmark, Norway, Austria, Switzerland, Slovakia, Croatia, Romania, Hungary, Slovenia, Ireland, Iceland, Luxembourg, Liechtenstein, San Marino, Monaco, Andorra, Vatican City; the up-to-date list is under Countries): with country_code or country the suggestions come from that country's register, and the text is written the way people write it there — street, number, postcode and city even in a single field: «kalverstraat 92 amst», «92 rue de rivoli paris», in the Netherlands «1012PH 92». Every response carries parsed, i.e. how the server read the street (street), the number (house_number) and the postcode (postcode): your form knows where the number goes without knowing the country's rules. Suggestions carry street, city, locality where there is one, and source (registry, or osm where the national register does not publish); the postcode and the house numbers are never in the suggestions: they come with the selection, which goes through the engine and returns the same record as /contact (postcode confirmed by the register, geo at the house number). With the selection you can also pass postcode, the one written in the form. attribution is the source notice to display next to the suggestions: the register's licence requires it. If the country is missing IT is assumed; for a country not in service the response is still HTTP 200 with supported:false and hints:[], without creating the session or charging anything. A session belongs to one country: if it changes, the client generates a new UUID (otherwise 409 session_country).
curl -X POST https://www.radaraddress.com/api/v1/suggest \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"session":"CLIENT-UUID","q":"kalverstraat 92 amst","country_code":"NL"}'
{"ok":true,"phase":"streets",
"hints":[{"type":"street","id":642154,"name":"Kalverstraat","municipality":"Amsterdam","area":"","source":"registry"}],
"parsed":{"street":"kalverstraat","house_number":"92","postcode":""},
"attribution":"Bron: Kadaster — BAG"}
Large volumes: the asynchronous job
Normal calls respond immediately, and for that reason they have a cap on items — an HTTP request that lasts minutes is no use to anyone. The caps follow how expensive the operation is:
| Endpoint | Items per call | Why |
|---|---|---|
/phone | 5.000 | immediate check |
/tax-code, /enrichment | 2.000 | fast check |
/contact, /email, /dedupe | 500 | full check |
/website | 100 | one connection to the site per address |
Beyond those numbers there is no need to split the list by hand: add "async": true and the call returns immediately with a code, while processing goes into the same queue as file uploads. Up to 100,000 items per call, and in My jobs a single one appears.
curl -X POST https://www.radaraddress.com/api/v1/contact \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"async":true,"items":[ … 100,000 contacts … ]}'
{"ok":true,"job":"369e46e9e7bb…","state":"queued","rows":100000}
Then you read it back when needed, and the result comes as JSON or as CSV:
curl 'https://www.radaraddress.com/api/v1/contact?job=369e46e9e7bb…' \
-H 'Authorization: Bearer YOUR_TOKEN'
{"ok":true,"state":"completed","rows":100000,"done":100000,
"outcomes":{"ok":1240,"modified":95300,"to_check":2100,"unresolved":1360}}
curl 'https://www.radaraddress.com/api/v1/contact?job=369e46e9e7bb…&format=csv' \
-H 'Authorization: Bearer YOUR_TOKEN' -o result.csv
Under 5,000 rows the result also comes back in results inside the JSON; above, it is downloaded as CSV. The charge happens when the job is queued, and the unprocessed part returns to your balance if the job stops. The token must belong to an account: the job ends up in your queue.
Outcome codes
Every response includes the outcome field: a list of keywords, empty when there is nothing to report. The pattern is always <conditions> [MODIFIED <types>] — conditions first, changes at the end — and it applies to every service. Next to it you find outcome_label (or kind) with ok, modified, warning, error, and reason with a one-line explanation. The codes are identifiers: they are compared, not translated; with "language": "it" the Italian codes come out (LOCALITA_NON_TROVATA MODIFICATO CAP INDIRIZZO), the same ones word for word.
Address (contact verification)
| Code | Meaning |
|---|---|
OK | No change needed, address already correct (empty outcome) |
MODIFIED | Address normalised, followed by the fields changed: POSTCODE, PROVINCE, CITY/CITY_FORM, STREET/STREET_FORM, BUILDING (the _FORM suffix = only format/accents, the value was already correct). The field schema is <conditions> [MODIFIED <types>]: any conditions come first, MODIFIED and its types at the end |
PO_BOX | PO box delivery recognised (not a street): form CASELLA POSTALE n |
LOCALITY_WITHOUT_STREET | The address is a locality without a street name; delivery is still possible |
CITY_NOT_FOUND | Municipality not recognised |
CITY_AMBIGUOUS | Municipality name present in more than one province |
STREET_NOT_FOUND | Street not registered for this city |
STREET_AMBIGUOUS | Street name present in more than one area of the municipality |
STREET_TYPE_MISSING | Street type not recognisable (Via/Corso/Piazza… missing) |
HOUSE_NUMBER_MISSING | House number missing or invalid |
HOUSE_NUMBER_INVALID_FORMAT | House number in an unrecognised form |
POSTCODE_UNCONFIRMED | The street exists but there is no confirmation of the postcode for that number: the one given stays, if it is one of the city's |
POSTCODE_PRESUMED | The street exists and we set the postcode ourselves without confirmation from the house number: the street has more than one and the number does not decide, or none was given and it comes from the nearby house numbers |
INCOMPLETE_DATA | Not enough information to normalise |
FOREIGN | Non-Italian address, in the postal form of its country; where the national register is in service, street and house number are verified, and the message says so (Italian only) |
HOUSE_NUMBER_NOT_FOUND | Foreign: the street exists in the national register, the given house number does not (only where the register has every house number: from a partial source or from OpenStreetMap a missing number is not a verdict) |
POSTCODE_INVALID_FORMAT | Foreign: the postcode is not in the form used in the country given |
COUNTRY_UNRESOLVED | Foreign: the country written in the address is not in the ISO 3166-1 catalogue |
Italian tax code
| Code | Meaning |
|---|---|
TAX_CODE_INVALID | The code fails the checks; reason says which (check character, length, month, date, municipality) and suggestion proposes the correct form when it can be reconstructed |
TAX_CODE_MISMATCH | The code is valid but does not match the surname, first name, sex or date in the request |
MODIFIED TAX_CODE | The check character was missing: recomputed from the first 15 |
MODIFIED TAX_CODE_FORM | Only the form was cleaned up (capitals, spaces) |
GENERATED | Endpoint /tax-code, action generate: code computed from the personal data |
MATCH / MISMATCH | Endpoint /tax-code, action compare: the code matches the data or not; differences lists the fields that do not |
On the /tax-code endpoint the same checks have codes of their own — TAX_CODE_CHECK_DIGIT_WRONG, TAX_CODE_CHECK_DIGIT_MISSING, TAX_CODE_LENGTH_WRONG, TAX_CODE_FORMAT_WRONG, TAX_CODE_MONTH_WRONG, TAX_CODE_DATE_INVALID, TAX_CODE_PLACE_UNKNOWN, TAX_CODE_EMPTY — because there the tax code is the object of the check, not a field of the record.
Enrichment
| Code | Meaning |
|---|---|
ENRICHED | Natural person: gender inferred or confirmed, title and salutations set |
LEGAL_PERSON | Company or organisation: heading treated as a company name (Spett.le) |
PARTIAL | First name missing, or gender not inferable from the name: neutral salutation |
| Code | Meaning |
|---|---|
EMAIL_INVALID | Invalid syntax |
EMAIL_DOMAIN_NOT_FOUND | The domain does not receive email (no MX/A record) |
EMAIL_TYPO | Possible typo in the domain: the correction is a proposal, in suggestion and in the normalised field |
MODIFIED EMAIL_DOMAIN | Certain typo in the domain, corrected: corrected_from carries what was written |
EMAIL_DISPOSABLE | Disposable email domain |
EMAIL_ROLE_BASED | Organisation address (info@, orders@), not a person's. It is a note, not an error |
EMAIL_EMPTY | No address in the field |
MODIFIED EMAIL_FORM | Only the form was cleaned up (spaces, capitals) |
Phone
| Code | Meaning |
|---|---|
PHONE_INVALID | Not a recognisable number |
PHONE_LENGTH_ANOMALOUS | Number of digits incompatible with the numbering plan |
PHONE_OUT_OF_PLAN | Does not start with 0 (landline) or 3 (mobile) |
PHONE_FOREIGN | Number with a non-Italian international prefix (or a national number of a foreign record): we check only its form, in E.164 |
PHONE_SPECIAL | Toll-free or premium-rate number: not a personal contact |
PHONE_SERVICE | Public utility number (112, 118…) |
PHONE_WITH_EXTENSION | The field also held an extension or a note: we check only the number |
PHONE_EMPTY | No number in the field |
MODIFIED PHONE_FORM | Only the form was cleaned up (spaces, dots, prefix) |
Website
| Code | Meaning |
|---|---|
WEBSITE_INVALID | Not a correctly written web address |
WEBSITE_DOMAIN_NOT_FOUND | The domain does not exist (no DNS record) |
WEBSITE_NOT_RESPONDING | The domain exists but no server responds |
WEBSITE_PAGE_NOT_FOUND | The site responds but the page is not there (404/410) |
WEBSITE_ACCESS_DENIED | The site denies access (401/403): often a bot protection |
WEBSITE_CERTIFICATE_INVALID | The site responds but the certificate cannot be verified |
WEBSITE_RESPONSE_ANOMALOUS | Unexpected response code |
WEBSITE_EMPTY | No address in the field |
MODIFIED WEBSITE_FORM | Address completed (scheme, www) without changing its substance |
What each call uses
Every call uses operations from the service's counter: contact verification (/contact, /suggest on selection), deduplication (/dedupe) and light operations (/email, /phone, /website, /enrichment, /tax-code beyond the allowance). Operations are bought as packs that stay, or with a monthly subscription; the price per 1,000 falls with the size and is on the pricing page. Every month 50 contact verifications, 100 records in deduplication and 500 light operations are free.
Every response says what it used and from where: the credit field of the JSON (counter, charged, free, subscription and packs with the operations taken and the remainder, available, auto_topup with the number of packs bought automatically, note) and the headers X-RA-Charged, X-RA-Available and, when it steps in, X-RA-Auto-Topup. If the available operations don't cover the call and the counter's auto-top-up is not active (or fails), the response is 402 payment_required and nothing is processed.
To know how many operations are left without using any there is GET /api/v1/credit. For each of the three counters (contact, dedupe, light) it returns available, the free ones (free) left in the month and the date they reset (the 1st), the subscription (remainder, size, renewal), the active packs one by one with what is left (they don't expire) and how many you bought, the auto_topup (active, size, cap and spent this month in euro), the usage (used this month, totals, last use) and above all the state, because a zero on its own doesn't say whether you ran out or you don't use that service: never_used, free (never bought, working within the free monthly ones), free_used_up, active, awaiting_renewal, used_up (you bought in the past and nothing is left: top up), with an warning. At the top needs_topup lists the counters that need action and endpoint says which counter each endpoint uses. Requires an account token.
curl https://www.radaraddress.com/api/v1/credit -H 'Authorization: Bearer YOUR_TOKEN'
→ { "ok": true, "counters": { "contact": { "available": 48250, "free": { "remaining": 50, "month": 50 },
"subscription": { "left": 23200, "size": 25000, "renews_on": "2026-10-03" }, "packs": { "left": 25000, "active_packs": [ … ] },
"state": "active", "warning": "" }, "dedupe": { "state": "never_used", "warning": "Counter never used: zero does not mean used up. …", … },
"light": { … } }, "needs_topup": [], "endpoint": { "contact": "contact", "email": "light", … } }
| Endpoint | Counter | Operations |
|---|---|---|
/contact | Contact verification | 1 per record (address, name and tax code together) |
/suggest | Contact verification | 1 per address selected (typing is free) |
/dedupe | Deduplication | 1 per record (normalisation included) |
/tax-code | Light operations | 1 per code, beyond the allowance |
/email | Light operations | 1 per email |
/phone | Light operations | 1 per number |
/website | Light operations | 1 per site |
/enrichment | Light operations | 1 per name |
You pay per item verified, not per call: if a field holds two phone numbers or two emails, we verify them all (up to three per row) and each one pays its price. A batch call uses as much as the operations it contains. If the credit is used up and auto-top-up is not active, the API answers HTTP 402 (insufficient credit); past the rate limit it answers HTTP 429 with retry_after. You buy a pack from your account area, or activate a subscription to pay less per operation.
Rate limit
60 requests a minute on single calls, 10 a minute on batch calls, 300 a minute on autocomplete.
Measured on the production API with twenty parallel calls: about 200 verifications per second, median under 60 milliseconds.
One count
The API, the website and batch jobs draw on the same counters: a pack is valid everywhere.
Versioning
Endpoints are versioned (/api/v1/): when a new version comes out, the old one stays up and the date it is switched off is announced in good time.
Ready to integrate?
Register the account, generate your token, buy the operations you need and make the first call in under a minute.
Create your tokenRelated questions
- What is address normalization, and how is it done?
- Is there an API to normalise addresses?
- How do I add address autocomplete to my form?
- How do I verify shipping addresses in my online shop?
- How do I get the coordinates of an address?
- DAWA shuts down on 1 October: what do I do with Danish addresses?
All the questions, with the answer, in Questions and answers.