Developers usually need one of two different things from a ZIP code API: a quick city and state for a five-digit code, or a verified, standardized delivery address with ZIP+4. The first can be served from a reference dataset; the second requires an address verification service. Choosing the right one avoids both unnecessary cost and false confidence.
Option 1: a postal reference dataset
For city, state, county association and an approximate coordinate, a downloadable dataset such as GeoNames postal codes (Creative Commons Attribution) is enough. Load it into your own database with the ZIP stored as a five-character string, and serve lookups from there. This site works the same way; see About our ZIP code data for the limits.
Option 2: an address verification API
To confirm that a street address exists, standardize it and return ZIP+4, use the USPS Addresses API, which requires registration and OAuth 2.0 credentials, or a commercial provider licensed to use USPS data. Keep the credentials on your server, never in browser code, and read the provider's permitted-use terms before caching results.
Design the response honestly
Return distinct outcomes rather than a single yes/no:
{ "status": "verified" | "candidates" | "no_match" | "unavailable",
"zip5": "02108", "zip4": "1234",
"candidates": [ ... ] }A provider outage is “unavailable,” not “invalid address.” Never fabricate a ZIP+4 from geography or random digits, and show the standardized address to the user before accepting it.
Validation and normalization
- Accept
^\d{5}(?:-\d{4})?$; strip spaces around the hyphen. - Preserve leading zeros end to end: database column type, JSON strings, CSV exports.
- Trim and title-case city names for display, but match on normalized values.
Test with the ZIP code validator and sample real codes with the random ZIP code generator.
Performance, caching and privacy
ZIP-level results change slowly, so cache them for days. Address-level results should be cached only as the provider allows. Apply rate limits per client, and do not log full residential addresses by default. For bulk jobs, batch requests and process in the background rather than in the request path; the bulk lookup shows a browser-side example.