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
EnglishItalianEnglishItalian
accountaccount membersmembri
actionazione methodmetodo
activated_onattivato_il metricmetrico
activeattiva min_levellivello_minimo
active_packsattivi missingmancano
addressindirizzo modifiedmodificati
address_keyindirizzo_confronto monthmese
addressesindirizzi monthly_cap_eurtetto_mese_eur
afterdopo multiple_postcodesmulticap
ageeta municipalitycomune
agreementconsenso municipality_codecomune_codice
ambiguous_yearanno_ambiguo municipality_code_typecomune_codice_tipo
areaarea n_recordsn_anagrafiche
asyncasincrono name_idnome_id
auto_topupauto_ricarica name_originalnome_originale
availabledisponibile national_numbernazionale
beforeprima nearbyvicino
belfiore_codecodice_belfiore needednecessarie
birth_countrynazione_nascita needs_topupda_ricaricare
birth_datedata_nascita networkrete
birth_placecomune_nascita normalizednormalizzato
birth_place_nameluogo_nascita normalized_postalnormalizzato_postale
birth_provinceprovincia_nascita not_foundnon_trovati
born_abroadnato_estero notenota
boughtcomprati notesnote
buildingedificio numbernumero
cadastral_codecatastale numbersnumeri
canonical_addressindirizzo_canonico occurrencesoccorrenze
canonical_citylocalita_canonica operationselaborazioni
care_ofpresso operatoroperatore
certificate_okcertificato_ok originorigine
changedmodificato otheraltro
changesmodifiche outcomeesito
chargedconsumate outcome_labelesito_label
checkcontrolla outcomesesiti
check_digitcontrollo overall_cap_eurtetto_globale_eur
citylocalita packspacchetti
city_keylocalita_confronto parsedletto
city_passescicli_localita phasefase
city_typelocalita_tipo phonetelefono
classclasse phone2telefono2
codecodice phone3telefono3
colourcolore phonestelefoni
commentscommenti placeluogo
conditionscondizioni positionposizione
confidenceconfidenza postcodecap
confirmedconfermato postcode_checkcap_verifica
consolidateconsolida precisionprecisione
contactcontatto preserve_originalpreserva_originale
contact_personreferente presumedpresunto
corrected_fromcorretta_da processedlavorato
countercontatore processed_atdatalav
counterscontatori professionqualifica
countriesnazioni provenanceprovenienza
countrynazione provinceprovincia
country_codenazione_iso2 provincial_capitalcapoluogo
country_originalnazione_originale reachableraggiungibile
country_prefixprefisso_paese reasonmotivo
createdcreato record_outcomeesito_record
creditcredito recordsanagrafiche
dedupededuplica redirectsredirect
detaildettaglio referenceriferimento
differencesdifferenze reference_idriferimento_id
discardedscartati remainingrimaste
disposableusa_e_getta renews_onsi_rinnova_il
districtquartiere reset_onsi_azzerano_il
domain_checkeddominio_verificato rowsrighe
domain_existsdominio_esiste salutationsaluto
donefatte savesalva
duration_msdurata_ms segment_passescicli_arcostradale
e164formato_e164 sessionsessione
educationtitolo_studio shortbreve
entriesschede singlessingoli
errorerrore sizetaglia
existsesiste sourcefonte
expiryscadenza specificityspecificita
explanationsspiegazioni spent_month_eurspeso_mese_eur
extensionestensione spent_overall_month_eurspeso_globale_mese_eur
fieldcampo statestato
final_urlurl_finale statesstati
first_namenome streetstrada
foreignesteri street_idvia_id
foreign_addressestero street_nametoponimo
formal_salutationsaluto_formale street_passescicli_toponimo
formatformato street_proper_nameduf
foundtrovato street_typedug
freegratuite sub_addresssubindirizzo
free_forevergratis_per_sempre sub_address_confirmedsubindirizzo_confermato
full_namenominativo subject_typetipo_soggetto
gendersesso subscriptionabbonamento
gender_sourcefonte_sesso suffixesponente
genericgenerica suggestionsuggerimento
groupgruppo summaryriepilogo
groupsgruppi supportedsupportato
hamletfrazione syntaxsintassi
hintssuggerimenti tax_codecodice_fiscale
homocodeomocodo tax_code_outcomecodice_fiscale_esito
house_numbercivico territoryterritorio
house_number_labelcivico_label texttesto
house_number_verifiedcivico_verificato time_mstempo_ms
house_numberscivici titletitolo
informal_salutationsaluto_informale to_checkda_controllare
iso2codice_iso2 totaltotali
iso3codice_iso3 towncitta
istat_codeistat truncatedtroncato
itemselementi typetipo
joblavoro type_normalizedtipo_norm
languagelingua typestipi
languageslingue unresolvednon_risolti
last_namecognome url_normalizedurl_normalizzato
last_onultimo_il usedusate
last_usedultimo_uso validvalida
leftresiduo valuevalore
legacy_defaultdefault_storico warningavviso
levellivello warningsavvisi
lightleggere websitesito
longlungo website2sito2
long_namenome_esteso website3sito3
main_recordprincipale websitessiti
matchcorrisponde zonedzonato
matchesabbinamenti

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

POST /api/v1/contact

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

POST /api/v1/dedupe

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.

Deduplication parameters
ParameterTypeDescription
recordsarrayRequired. 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, emailsarrayOptional, 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_codestringOptional, 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
referencearrayOptional: two-list mode. Each record is searched in the reference; response with matches and not_found
min_levelstringcertain | 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
foreignstringdeclared (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)
saveboolDefault 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

POST /api/v1/tax-code

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

POST /api/v1/enrichment

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

POST /api/v1/email

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

POST /api/v1/phone

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

POST /api/v1/website

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

POST /api/v1/suggest

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:

Item caps per call
EndpointItems per callWhy
/phone5.000immediate check
/tax-code, /enrichment2.000fast check
/contact, /email, /dedupe500full check
/website100one 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)

Outcome codes — address (contact verification)
CodeMeaning
OKNo change needed, address already correct (empty outcome)
MODIFIEDAddress 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_BOXPO box delivery recognised (not a street): form CASELLA POSTALE n
LOCALITY_WITHOUT_STREETThe address is a locality without a street name; delivery is still possible
CITY_NOT_FOUNDMunicipality not recognised
CITY_AMBIGUOUSMunicipality name present in more than one province
STREET_NOT_FOUNDStreet not registered for this city
STREET_AMBIGUOUSStreet name present in more than one area of the municipality
STREET_TYPE_MISSINGStreet type not recognisable (Via/Corso/Piazza… missing)
HOUSE_NUMBER_MISSINGHouse number missing or invalid
HOUSE_NUMBER_INVALID_FORMATHouse number in an unrecognised form
POSTCODE_UNCONFIRMEDThe 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_PRESUMEDThe 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_DATANot enough information to normalise
FOREIGNNon-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_FOUNDForeign: 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_FORMATForeign: the postcode is not in the form used in the country given
COUNTRY_UNRESOLVEDForeign: the country written in the address is not in the ISO 3166-1 catalogue

Italian tax code

Outcome codes — Italian tax code
CodeMeaning
TAX_CODE_INVALIDThe 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_MISMATCHThe code is valid but does not match the surname, first name, sex or date in the request
MODIFIED TAX_CODEThe check character was missing: recomputed from the first 15
MODIFIED TAX_CODE_FORMOnly the form was cleaned up (capitals, spaces)
GENERATEDEndpoint /tax-code, action generate: code computed from the personal data
MATCH / MISMATCHEndpoint /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

Outcome codes — enrichment
CodeMeaning
ENRICHEDNatural person: gender inferred or confirmed, title and salutations set
LEGAL_PERSONCompany or organisation: heading treated as a company name (Spett.le)
PARTIALFirst name missing, or gender not inferable from the name: neutral salutation

Email

Outcome codes — email
CodeMeaning
EMAIL_INVALIDInvalid syntax
EMAIL_DOMAIN_NOT_FOUNDThe domain does not receive email (no MX/A record)
EMAIL_TYPOPossible typo in the domain: the correction is a proposal, in suggestion and in the normalised field
MODIFIED EMAIL_DOMAINCertain typo in the domain, corrected: corrected_from carries what was written
EMAIL_DISPOSABLEDisposable email domain
EMAIL_ROLE_BASEDOrganisation address (info@, orders@), not a person's. It is a note, not an error
EMAIL_EMPTYNo address in the field
MODIFIED EMAIL_FORMOnly the form was cleaned up (spaces, capitals)

Phone

Outcome codes — phone
CodeMeaning
PHONE_INVALIDNot a recognisable number
PHONE_LENGTH_ANOMALOUSNumber of digits incompatible with the numbering plan
PHONE_OUT_OF_PLANDoes not start with 0 (landline) or 3 (mobile)
PHONE_FOREIGNNumber with a non-Italian international prefix (or a national number of a foreign record): we check only its form, in E.164
PHONE_SPECIALToll-free or premium-rate number: not a personal contact
PHONE_SERVICEPublic utility number (112, 118…)
PHONE_WITH_EXTENSIONThe field also held an extension or a note: we check only the number
PHONE_EMPTYNo number in the field
MODIFIED PHONE_FORMOnly the form was cleaned up (spaces, dots, prefix)

Website

Outcome codes — website
CodeMeaning
WEBSITE_INVALIDNot a correctly written web address
WEBSITE_DOMAIN_NOT_FOUNDThe domain does not exist (no DNS record)
WEBSITE_NOT_RESPONDINGThe domain exists but no server responds
WEBSITE_PAGE_NOT_FOUNDThe site responds but the page is not there (404/410)
WEBSITE_ACCESS_DENIEDThe site denies access (401/403): often a bot protection
WEBSITE_CERTIFICATE_INVALIDThe site responds but the certificate cannot be verified
WEBSITE_RESPONSE_ANOMALOUSUnexpected response code
WEBSITE_EMPTYNo address in the field
MODIFIED WEBSITE_FORMAddress 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", … } }
Counter used by each endpoint
EndpointCounterOperations
/contactContact verification1 per record (address, name and tax code together)
/suggestContact verification1 per address selected (typing is free)
/dedupeDeduplication1 per record (normalisation included)
/tax-codeLight operations1 per code, beyond the allowance
/emailLight operations1 per email
/phoneLight operations1 per number
/websiteLight operations1 per site
/enrichmentLight operations1 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 token

See pricing