Guide

How to Test an Address API's Accuracy

Vendor accuracy claims are rarely comparable. Here is a method you can run yourself on any address verification or parsing API, with the numbers to record and what good looks like.

Elizabeth Parker
September 27, 2026 · 8 min read · Last reviewed

To test an address API's accuracy, take real addresses you can confirm exist, damage them in controlled ways (typos, missing fields, reordering), add a few fake ones, and send the same random sample to every API. Then score five numbers: how often it recovered the right address, how often it was wrong without warning, how often it flagged its own doubt, how often it returned nothing, and whether it refused the fakes.

Clean addresses tell you almost nothing. Every serious API gets a well-formed address right. The differences show up on the input real users type, and on what the API does when it is not sure.

The five numbers to measure

NumberWhat it meansWhy it matters
Recovery rateShare of damaged inputs where the API returned the correct, standardized addressThe headline number, but only meaningful per damage type
Silent substitution rateWrong address returned as an exact, high-confidence match, no warningThe costly error: nothing downstream catches it. Should be close to zero
Flagged wrong rateWrong address, but marked as corrected, approximate or low confidenceAcceptable if you route flagged results to review
Empty result rateAPI returned nothing or rejected the inputA visible miss; safer than a silent one
Fake-address refusal rateShare of addresses with a non-existent house number that the API refusedAn API that always answers will invent matches for addresses that do not exist

Step by step

1. Build a truth set

Start from real addresses: your own customer data, or a public list. Run each one in its clean form first and keep only the ones that resolve to a real deliverable address. In our own benchmark only 72.8% of a raw sample passed this screen. Scoring the rest would measure the quality of your list, not the API.

2. Damage it in controlled ways

Generate variants of each confirmed address, one damage type at a time, so you can report results per type:

Damage typeExample inputCorrect answer
Formatting only52 FEDERAL RD DANBURY CT 0681052 Federal Rd, Danbury, CT 06810-6129
Single typo63 MAUN RD, GILL, MA 0135463 Main Rd, Gill, MA 01354
Missing ZIP108 J RD, VOSS, TX108 J Rd, Voss, TX 76888
Missing city1025 BRIAR AVE OH 431601025 Briar Ave, Washington Court House, OH 43160-1007
Sound-alike spelling2533 TAPHT AVE, OREGON, OH 436162533 Taft Ave, Oregon, OH 43616-3829
Reordered fields79252 TX QUANAH EDDY ST 16001600 Eddy St, Quanah, TX 79252-6428
Several problems at once2 emreald ln doevr new hmapshire2 Emerald Ln, Dover, NH 03820

3. Add fake addresses

Take confirmed addresses and change the house number to one that does not exist on that street. A good API refuses these. An API tuned to always return something will snap them to the nearest real address and call it a match.

4. Sample at random, run everything the same way

Draw your test inputs at random from the full set of variants, not by hand, and send the identical sample to every API you compare. Save the complete response, including any match level, confidence score and per-field flags, not just the address.

5. Score the five numbers, per damage type

A short scoring loop is enough. This one works with any API that returns a standardized address and some confidence signal:

def score(results):
    """results: list of dicts with 'expected', 'returned', 'confident' (bool), 'is_fake' (bool)"""
    real = [r for r in results if not r["is_fake"]]
    fake = [r for r in results if r["is_fake"]]
    n = len(real)
    recovered = sum(r["returned"] == r["expected"] for r in real)
    silent    = sum(r["returned"] and r["returned"] != r["expected"] and r["confident"] for r in real)
    flagged   = sum(r["returned"] and r["returned"] != r["expected"] and not r["confident"] for r in real)
    empty     = sum(not r["returned"] for r in real)
    refused   = sum(not r["returned"] for r in fake)
    return {
        "recovery":        recovered / n,
        "silent_wrong":    silent / n,
        "flagged_wrong":   flagged / n,
        "empty":           empty / n,
        "fake_refusal":    refused / max(len(fake), 1),
    }

Normalize both addresses the same way before comparing (case, abbreviations, ZIP+4 present or not), or you will count formatting differences as errors.

How many addresses you need

Small samples make close results look different. The margin of error at 95% confidence for a measured accuracy near 95%:

Addresses per damage typeMargin of errorWhat you can tell apart
100about ±4.3 pointsOnly large gaps, such as 85% versus 95%
1,000about ±1.4 pointsGaps of 3 points or more
10,000about ±0.4 pointsGaps of 1 point

For silent substitutions, which should be rare, you need thousands of inputs to see them at all. At 1 in 700, a 100-address test will usually show zero.

Mistakes that make results meaningless

  • Testing only clean addresses. Every API scores near 100% on them. You learn nothing.
  • Counting any answer as a success. An API that always returns something will look great on recovery and hide its silent errors.
  • Ignoring the confidence fields. The same wrong answer is harmless if it is flagged and costly if it is not.
  • Using the vendor's demo addresses. They are chosen to work. Use your own data.
  • Reporting one blended number. A mix heavy on formatting noise will beat a mix heavy on typos. Report per damage type.
  • Scoring unconfirmed addresses. If the clean address is not real, no API can recover it. Screen first.

Worked example: our own results

We ran this method on the sthan.io parser and published the results in August 2026: 29,966 random variants of screened addresses, 26,943 of them scored against confirmed real addresses. Full details are in the benchmark write-up.

NumberResult
Recovery, formatting-only damage99.9%
Recovery, a typo or a dropped field96.2%
Recovery, reordered fields and multiple errors89.7%
Recovery, several severe problems at once71.2%
Silent substitutionat most 0.14% (about 1 in 700), none with the wrong state
Flagged wrong3.4%
Empty result7.9%
Fake-address refusal99.9%

Run the same test on any API you are considering, including ours. You can start with the Address Verification API free tier of 100 requests a month, or the Address Parser for freeform input.

Frequently Asked Questions

At least 1,000 per damage type you care about. At 95% accuracy, a sample of 100 has a margin of about ±4 points, so two APIs 3 points apart cannot be told apart. At 1,000 the margin is about ±1.4 points.
The API returns a different address than the one meant and reports it as an exact, high-confidence match with no warning. It is the most expensive error because nothing downstream catches it.
Yes. When an address cannot be worked out, an empty or rejected result is safer than a confident guess. Test it directly with addresses whose house number does not exist.
Before you choose a vendor, and again once a year or after any major change on their side. Keep your truth set and scoring script so a retest takes minutes.

Run the test on sthan.io

100 free verification or parsing requests every month. No credit card.

Elizabeth Parker
Written by Elizabeth Parker

Elizabeth Parker writes about how verification works, deliverability statuses, missing unit numbers, and how to measure an address API's accuracy.

More from Elizabeth Parker