How Accurate Is US Address Parsing on Messy Input?
We corrupted thousands of real US addresses with typos, dropped fields, and reordered components, then measured how often the sthan.io parser recovered the original, how often it got one wrong, and whether it warned us when it was unsure.
Real addresses get typed badly. People swap letters, forget the ZIP, put things in the wrong order, and spell streets the way they sound. So the useful question about an address parser is a simple one: what does it do with the mess?
We took 200,000 real US delivery addresses and broke them on purpose: typos, missing city, state, or ZIP, scrambled order, sound-alike street spellings. Then we asked our parser to work out the original address from each broken version. Three things stood out.
- On realistic damage, it recovered the right address 96% to 99.9% of the time. Formatting-only corruption (casing, separators, whitespace): 99.9%. A typo or a dropped field: 96%. Only under deliberately extreme corruption, several severe problems stacked on one input, does it drop to 71%. The average across everything, including those extreme cases, is 88.5%.
- It almost never gives a wrong answer that looks right. At most 1 input in 700 got a wrong answer with no warning. Not one of those had the wrong state.
- It tells you when it guessed. Corrections come back marked as corrected or approximate, with a lower confidence score. When it cannot work the address out at all, it says so instead of guessing.
One thing before the numbers. We only scored addresses we could first confirm are real. If an address cannot be verified against postal reference data, no parser can recover a broken version of it, so counting those would only muddy the result. The rest of this post walks through each number.
What we tested
We started from a large set of real US delivery addresses and generated controlled corruptions of each one, spanning 31 corruption types ordered from mild to severe:
- Single and multiple typos, letter transpositions, and phonetic street spellings
- Dropped city, dropped state, dropped ZIP, and dropped street directionals
- Reordered fields, including fully reversed order and state-before-city forms
- Separator and casing noise: no commas, semicolons, extra spaces, all lowercase, all uppercase
The test had two steps. Step one: check each address in its clean form and keep only the ones that resolve to a real postal address. That was 72.8% of our sample. Step two: take 29,966 corrupted variants of those verified addresses, sampled at random from more than a million we generated, and send each one to the live parser in its maximum-recovery mode. Random selection matters here. It means the mix looks like the noise a real integration sees, and nothing was hand-picked to look good.
To keep the numbers honest, the parser had never seen any of these addresses as reference data. A parser tested on data it was built from always looks better than it really is. And because every address in the test is verifiably real, every miss in this report is a genuine miss. There is no fine print behind the number.
Recovery: the right address from a corrupted one
Recovery means one thing: given a corrupted input, the parser returned the correct address, standardized. One reading note before the table. The corruption classes are not equally common in real life. Casing problems, separator noise, a typo, a dropped field: that is the overwhelming majority of what production traffic looks like, and it recovers at 96% to 99.9%. The compound class, several severe corruptions deliberately stacked on one input, is a stress test we built to find the breaking point. It drags the average down, and we report it anyway. Here is how it did:
| Metric | Result |
|---|---|
| Corrupted inputs tested (all verified real addresses) | 26,943 |
| Recovered: formatting-only corruption (casing, separators, whitespace) | 99.9% |
| Recovered: single-field damage (a typo, a dropped field) | 96.2% |
| Recovered: structural damage (reordered fields, multiple errors) | 89.7% |
| Recovered: compound damage (several severe corruptions at once) | 71.2% |
| Recovered: average across all severities | 88.5% |
| Wrong answer with no warning | at most 0.14% (about 1 in 700); wrong state: zero |
| Wrong answer, but flagged as uncertain | 3.4% |
| Returned nothing instead of guessing | 7.9% |
None of this was measured on tidy inputs. Below are 20 real benchmark inputs the parser recovered correctly, five per severity class, color coded from compound damage down to formatting-only corruption. The middle column is exactly what went in. The last column is the match signal the parser returned: Exact when the address was never in doubt, and Near, Approximate, or Speculative with a confidence score when it had to correct or infer a field.
| Corruption | Input, as typed | Recovered | Signal |
|---|---|---|---|
| Compound damage several severe corruptions at once · 71.2% recovered in this class | |||
| Typos throughout, no ZIP | 2 emreald ln doevr new hmapshire |
2 Emerald Ln, Dover, NH 03820 | Approximate · 0.7 |
| Two typos, no ZIP | 18 CQDTER CT, ALOWN, TX |
18 Carter Ct, Allen, TX 75002-5286 | Approximate · 0.7 |
| Typos, random case, spaces | 312 cLraK sT CacYe soUth acRolIna 29033 |
312 Clark St, Cayce, SC 29033-3911 | Approximate · 0.7 |
| Two typos, no ZIP | 275 DKRCHSSTER AVE, BKSTLN, MA |
275 Dorchester Ave, South Boston, MA 02127-1809 | Approximate · 0.85 |
| Typos throughout, no ZIP | 617 csating cv montgomrey alabaam |
617 Casting Cv, Montgomery, AL 36108-1355 | Approximate · 0.7 |
| Structural damage reordered fields, multiple errors · 89.7% recovered in this class | |||
| Fully reversed order | 79252 TX QUANAH EDDY ST 1600 |
1600 Eddy St, Quanah, TX 79252-6428 | Approximate · 0.7 |
| ZIP first, city last | 50519-5025 608 2ND ST IA BODE |
608 2nd St, Bode, IA 50519-5025 | Exact |
| House number at the end | RD M, CORTEZ, CO 81321 24553 |
24553 Rd M, Cortez, CO 81321-8854 | Approximate · 0.7 |
| State and city swapped | 403 7TH ST, IA, BODE 50519-9501 |
403 7th St, Bode, IA 50519-9501 | Approximate · 0.7 |
| State first | IA, 3 G AVE W, ALBIA, 52531-1003 |
3 G Ave W, Albia, IA 52531-1003 | Exact |
| Single-field damage a typo or a dropped field · 96.2% recovered in this class | |||
| City missing | 1025 BRIAR AVE OH 43160 |
1025 Briar Ave, Washington Court House, OH 43160-1007 | Exact |
| ZIP missing | 108 J RD, VOSS, TX |
108 J Rd, Voss, TX 76888 | Exact |
| Misspelled city | 5 JUDY LN, CJIVO, CA 95926 |
5 Judy Ln, Chico, CA 95926-1714 | Approximate · 0.7 |
| Phonetic street spelling | 2533 TAPHT AVE, OREGON, OH 43616 |
2533 Taft Ave, Oregon, OH 43616-3829 | Speculative · 0.5 |
| Single typo | 63 MAUN RD, GILL, MA 01354 |
63 Main Rd, Gill, MA 01354 | Near · 0.9 |
| Formatting only casing, separators, whitespace · 99.9% recovered in this class | |||
| Noise prefix | Deliver to: 96 3RD ST, TROY, NY 12180 |
96 3rd St, Troy, NY 12180-4037 | Exact |
| Tabs for commas | 20161 BATTERY PARK RD, SMITHFIELD, VA 23430 |
20161 Battery Park Rd, Smithfield, VA 23430-5609 | Exact |
| No commas | 52 FEDERAL RD DANBURY CT 06810 |
52 Federal Rd, Danbury, CT 06810-6129 | Exact |
| Random mixed case | 1566 W CoMET rD, neW fRAnkLIn, OH 44216 |
1566 W Comet Rd, New Franklin, OH 44216 | Near · 0.9 |
| All lowercase | 49 river rd w, berlin, ma 01503 |
49 River Rd W, Berlin, MA 01503-1645 | Exact |
Notice two things. The parser also cleaned up the casing and filled in the ZIP+4 wherever it could. And look at the last column: formatting-only corruption came back Exact, while every typo, dropped field, and reorder came back marked as a correction. It never pretended a repaired address was an exact match.
When it is unsure, it tells you
A miss will rarely hurt you. You get no answer, you see it, you handle it. The dangerous case is a silent substitution: the parser returns a different address than the one you meant and stamps it as an exact, high-confidence match. No warning. That is how a package ends up at the wrong house while everyone believes the data was clean.
In this test, that happened on at most 37 inputs out of 26,943, about 1 in 700. We say "at most" because we scored against reference data that has flaws of its own, so some of those 37 answers may actually be fine. Not one of them had the wrong state. Everything else that was wrong or uncertain came with a warning. When the parser changes something, it lowers the match tier and the confidence score:
matchTierdrops fromExacttoNear,Approximate, orSpeculativeconfidencedrops below 1matchCodeflags which components were corrected or left unconfirmed
Here is one of the heavier inputs from the benchmark: a misspelled street, a misspelled city, a misspelled state, and no ZIP. This is the response it produced.
// input: "2 emreald ln doevr new hmapshire"
{
"fullAddress": "2 Emerald Ln, Dover, NH 03820",
"matchTier": "Approximate",
"confidence": 0.7,
"matchCode": {
"houseNumber": "Matched",
"street": "Corrected",
"city": "Inferred",
"state": "Matched",
"zipCode": "Inferred"
}
}
The parser corrected the street, inferred the city and ZIP, and did not pretend to be certain. It returned Approximate at confidence 0.7 with a per-field breakdown of exactly what it changed.
This lets you set a simple rule in your code. Accept exact, high-confidence results automatically. Send anything marked corrected or approximate to a review step. In this run, 918 answers were wrong and said so themselves: their tier and confidence told you up front to double-check them.
Refusing addresses that do not exist
A parser that always returns something is easy to build and dangerous to use. So we also fed it poisoned input: made-up house numbers, the wrong state for a ZIP, the wrong ZIP for a city, and flipped street directions. The right response there is to refuse, to flag the fix, or to restore the true address. The parser did one of those 99.5% of the time. For made-up house numbers specifically, it returned nothing 99.9% of the time instead of inventing a match.
It shows the same restraint on ordinary broken input. When too much of an address is destroyed, it returns nothing rather than guessing. That happened on 7.9% of the test, and the next section breaks down where.
Where recovery falls short
So where does the missing 11.5% go? Split it up: 7.9% refused to answer, 3.4% were wrong but flagged themselves, and at most 0.14% slipped through silently. Every address in the test is real, so the refusals are genuine misses. They pile up exactly where you would guess:
- Several typos and no ZIP. When the street and the city are both misspelled and there is no ZIP, there is not much left to work with. On those inputs alone, refusals run 31% to 38%. Could the parser guess? Sure. Guessing is how wrong-address databases get built, so it refuses instead. This is a deliberate choice, and it is the largest remaining group.
- Severely reordered input. Fully reversed and ZIP-first orderings still fail more than average when the reordering also strips the fields that anchor a search. This class got much smaller in recent releases and keeps shrinking.
We publish this because knowing where the parser fails is as useful as knowing where it works. It tells you exactly where to add a check in your own flow: if your form asks for a ZIP, you close most of the remaining gap.
What this means for your integration
- Trust the exact, high-confidence results. They are almost never silently wrong, so you can accept them without a human in the loop.
- Use the match tier and confidence as a routing signal. Send corrected or approximate answers to review or to a user confirmation step.
- Expect no answer on genuinely bad input. A refusal is information. Treat it as "ask the user to check," not as a bug.
- Provide a ZIP when you can. It is the single most valuable anchor when the rest of the input is noisy.
You can run the same kinds of inputs against the API yourself. The free tier includes 100 lookups every month, no card required.
Frequently Asked Questions
Try it on your own messy addresses
Run corrupted, real-world inputs against the sthan.io API. Get 100 free lookups every month.