"Does this address exist?" sounds like a yes-or-no question. It is not, and the products that answer it as one are quietly making things up. Whether a given building can be found depends on which register you asked, how much of the country that register covers, and how far into it the address could be followed. This guide is about reading that answer honestly: the three match levels, the three states of exists, and what each of them is worth in a decision.
Two fields, read together
Spaw answers existence with a pair.
match_level says how far the address was followed into the register: premise (the building itself), street (the street, not that number on it), postcode (the postcode area only), none (nothing matched), or null (no register was consulted at all).
exists turns that into a verdict, and it is deliberately hard to make it say no:
trueonly on apremisematch. The building itself was found.falseonly when the register that answered claims, in its own publisher's words, to list every address in its country — and does not carry this one.nullin every other case: no register covers the country, the register that does covers only part of it, or the address is a box with no street behind it.
Two more fields tell you who answered and how far to trust the answer: register names the dataset, and register_coverage reports complete or partial as its publisher describes it. Read exists against register_coverage every time.
The four answers, measured
These are real answers from the Swiss building-address directory, a register whose publisher describes its coverage as complete, run through the lookup on 5 September 2026.
| Address | match_level | exists | risk |
|---|---|---|---|
| Bahnhofstrasse 1, 8001 Zürich | premise | true | 0 |
| Bahnhofstrasse 9999, 8001 Zürich | street | false | 20 |
| Nonexistentstrasse 999, 8001 Zürich | postcode | false | 20 |
| Bahnhofstrasse 1, 9999 Zürich | none | false | 20 |
Read the second row carefully, because it is the interesting one. The street is real and was found; the house number on it is not. The answer keeps both facts — match_level: street and exists: false — instead of collapsing them, so you can tell "wrong number on a real street" (usually a typo, worth asking about) from "street nobody has heard of" (usually a fabrication, or a very new development).
The last row is the reverse: a real street and number with a postcode that does not belong to it. A complete register that carries neither the premise nor the street in that area answers false, and match_level: none tells you the search never got anywhere at all.
Why absence from a partial register proves nothing
Change the register and every false above becomes null.
Some national registers are contributed to voluntarily. The United States National Address Database is assembled from what states and local authorities choose to submit, and the French Base Adresse Nationale from what communes choose to publish; both describe their own coverage as partial. In a register like that, "not found" carries no information about the address at all. It might not exist. It might sit in a county that never sent its data.
So Spaw does not say false there. It says null, which means not evaluated — and the difference is enforced right through to the score: the not_in_register signal fires only when exists is false, so an address missing from a partial register contributes exactly zero points. There is no quiet penalty for living somewhere whose local authority did not take part.
This is the single most important line in this guide, so it is worth writing plainly: absence from a partial register is not evidence that an address does not exist. Any product that scores it as if it were is charging you for a bug.
Null is not no
The same discipline runs through every flag in the response, and it is worth mapping it into your own schema properly. null is not false. It means the dataset that would have answered is not installed for that country, or was not applicable to that address.
exists: null— no register answered. Say nothing about the building.is_po_box: null— the box naming for that country is not carried, so "not a box" was never established.is_mail_drop: null— neither the operator lists nor a company register covered it.deliverability_checked: false— the licensed delivery check did not run, sois_deliverable,is_cmra,is_vacantandis_residentialare allnull, and none of them is a "no".
If your database column is a boolean, you have already lost this distinction. Make the column nullable, or store the string.
Existence and validity are different questions
They arrive in different fields, and conflating them is the most common integration mistake.
Validity is whether the address stands as written for its own country: are the required lines there, does the postcode match the country's pattern, and — where a national directory is installed — does that postcode exist? A failure here answers valid: false with a typed reason such as missing_locality, invalid_postal_code or postcode_not_found, and it is free of charge. Something is wrong with the text, and a person can usually fix it.
Existence is whether the building is in the register. An address can be perfectly valid and still answer exists: false, which is why that case is a 20-point risk signal on a valid address rather than an invalid verdict. Nothing about the writing is wrong; the place just is not in the book.
Keep them in separate branches of your code. "Fix your address" and "we could not confirm this address" are different messages to send someone, and only one of them is their fault.
What to do with each answer
exists: true— the strongest confirmation open data offers. It is still not deliverability: a real building can be vacant, and whether post arrives is a licensed answer that this lookup does not make.exists: falsewithmatch_level: street— treat as a probable typo. Show the person the street you found and ask for the number again.exists: falsewithmatch_level: postcodeornone— worth a human look before shipping anything expensive. Combine it with the rest of the response rather than rejecting on it alone; new-build addresses appear in registers late, and register updates lag the ground.exists: null— carry on as if you had not asked. Use the parts of the answer that did run: the format, the delivery-point kind, the postcode status.- Never store a
nullas afalse, and never show a customer "this address does not exist" on the strength of a partial register.
Where the register facts come from
Every answer names its own sources. register gives the dataset, register_coverage the publisher's own description of it, and the sources block lists the version of each dataset installed when the answer was given — so an answer from last month can be checked against the file that produced it rather than against today's. The free postal address checker prints the whole block, including the coverage table for the datasets this server currently holds.
What to do next
- Try the four cases above on the free postal address checker and watch
match_levelchange whileexistsstays honest. - Read the address verification guide for every field, every reason and every weight.
- See what the postcode half of the answer can and cannot settle in a UK postcode is not an address.
- Understand how the 20 points fit into the rest in how Spaw computes the risk score.