Check a US shipping address before quoting or ordering against it. Does not create or store anything and asks no outside service: it checks the ZIP's shape (five digits or ZIP+4), the state against the USPS state, territory and military codes (a state's full name is accepted), and whether the ZIP's first three digits belong to that state, and flags a PO Box or military (APO/FPO/DPO) address. valid is false when any finding is an error — zip_format, state_unknown, or zip_state_mismatch (the ZIP belongs to a state that does not border the one named) — and POST /order and POST /orders/quote refuse such an address with a live key (422 address_invalid) and return the same findings as address_check. A ZIP from a neighbouring state is a warning (zip_state_border): a few ZIPs straddle a state line. A state written in a way the check does not know (it takes the codes, the full names, the usual abbreviations such as Calif. and W.Va., and names such as Washington, D.C. or USVI) is state_unknown, an error only when the ZIP does not settle it: when the ZIP's first three digits belong to one state it is a warning and normalized.state carries that state's code. normalized is the address in USPS style: upper case, no punctuation, ZIP+4 hyphenated, a unit typed into address1 moved to address2. Any country other than US is passed through with checked: false. It does not verify that the street or the house exists; a live POST /order does that with USPS where the deployment has it configured (the delivery_point_* codes on AddressFinding).
- Credential
- An API key in
x-api-key - Scopes
pricing:read
Parameters
Idempotency-Keyheader · string (uuid)Unique key for idempotent requests. If you retry a request with the same key within 24 hours, you receive the original response. The key is bound to the request payload: reusing it for a different payload returns 422
idempotency_key_reuserather than the original response, so a changed request is never silently dropped. The binding is over the JSON's content, not its bytes — keys in another order or other whitespace are the same request. Generate a new key for each new request. On an operation that moves money (an order, a batch, an item, a shipping or address change, a cancellation's refund), a key that cannot be checked because the idempotency ledger is unreachable is answered 503idempotency_unavailablewithRetry-After, and nothing runs or is charged: retry with the same key.
Request body application/json, required
namestringcompanystringaddress1stringrequirednon-empty
address2stringcitystringrequirednon-empty
statestringrequiredA USPS code (TX, PR, AE) or a state's full name.
non-empty
zipstringrequirednon-empty
countrystringISO 3166-1 alpha-2. Only US addresses are checked.
Default US · exactly 2 characters
phonestringemailstring (email)
Request
POST /api/v1/addresses/validate
curl -X POST "https://portal.printedsimply.com/api/v1/addresses/validate" \
-H "x-api-key: ps_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: validate-address-$(date +%s)" \
-d '{
"address1": "123 Main St. Apt 4",
"city": "Austin",
"state": "Texas",
"zip": "787011234",
"country": "US"
}'Response 200
The verdict, whatever it is: valid: false is an answer, not an error.
data
validbooleanrequiredFalse when any finding is an
error.checkedbooleanrequiredFalse for an address outside the US, which is passed through as written.
normalizedobjectrequiredThe address in USPS style: upper case, no punctuation, ZIP+4 hyphenated, the state as its code, a unit typed into address1 moved to address2. Name, company, phone and email are trimmed and otherwise as sent.
normalized.namestringnormalized.companystringnormalized.address1stringrequirednormalized.address2stringnormalized.citystringrequirednormalized.statestringrequirednormalized.zipstringrequirednormalized.countrystringrequirednormalized.phonestringnormalized.emailstringfindingsobject[]requiredfindings[].codestringrequiredThe
delivery_point_*codes come from delivery-point verification, which only a live POST /order, POST /orders/bulk entry or PUT /order/{id}/address runs, on a US address the other checks pass, and only where the deployment has USPS verification configured:delivery_point_not_found(error) — USPS answered that no delivery point exists at the street address;delivery_point_incomplete(warning) — the building exists but its unit is missing or not confirmed, or more than one address matched;delivery_point_confirmed(info). When USPS cannot be reached the order proceeds without them.One of
zip_formatstate_unknownzip_state_mismatchzip_state_borderzip_unassignedpo_boxmilitary_statemilitaryaddress2_splitnot_checkeddelivery_point_not_founddelivery_point_incompletedelivery_point_confirmedfindings[].severitystringrequirederror: the address cannot be right as written, and a live order or quote is refused with 422 address_invalid.warning: worth a look, never refused.info: what normalization did, or that nothing was checked.One of
errorwarninginfofindings[].fieldstringrequiredOne of
address1address2citystatezipcountryfindings[].messagestringrequired
meta
pageintegertotalintegerlimitintegerThe page size actually applied, which is not necessarily the one asked for — the operation's own ceiling wins.
pagesintegertotaloverlimit, rounded up.next_cursorstring | nullOn a cursor-paged operation: send this back as
cursorfor the next page. Null on the last page. Absent on operations that do not page by cursor.has_morebooleanOn a list operation whose
metaisListMeta: whether another page follows this one.
{
"status": "success",
"data": {
"valid": true,
"checked": true,
"normalized": {
"address1": "123 MAIN ST",
"city": "AUSTIN",
"state": "TX",
"zip": "78701-1234",
"country": "US",
"address2": "APT 4"
},
"findings": [
{
"code": "address2_split",
"severity": "info",
"field": "address2",
"message": "Moved \"APT 4\" from address1 to address2."
}
]
},
"request_id": "4e3753e1-48dd-4313-bfd8-48e4af4e116a"
}Errors
Invalid request body or parameters. When the request failed the operation's schema,
detailscarriesformErrors(problems with the request as a whole),fieldErrors(messages keyed by top-level field) anderrors: one{ pointer, code, message }per refused value,pointeran RFC 6901 JSON pointer into the body (or into the object of query parameters, for a query parameter) —/items/0/sku, with~written~0and/written~1, and""for the request as a whole. POST /v1/orders/bulk answerserrorsas one{ index, details }per refused order, eachdetailswith its own pointers from the request body (/orders/3/items/0/sku); POST /v1/orders/multi-ship keepserrorsas the{ formErrors, fieldErrors }it always published; POST /v1/mockups/generate and POST /v1/products/recommend keep their fields at the top ofdetails, witherrorsbeside them. A refusal of a rule the schema cannot state (an asset that does not exist, an expiry too soon) names it inmessageand carries noerrors.Missing, invalid or expired API key:
missing_api_key(none sent),invalid_api_key(unknown or revoked),key_expired(past the expiry set when it was created; create a new key).payload_too_large — the request body exceeded this route's byte cap (256 KB unless the route raises it with a written reason). It is refused before it is parsed, so nothing was created. Send fewer rows, or use the endpoint's own asynchronous door where it has one.
Rate limit exceeded. Two windows can produce this status and
error.codesays which:rate_limit_exceededfor the per-minute sliding window,daily_quota_exceededfor the plan's daily quota. Both carryRetry-After; the daily one also carriesX-Daily-LimitandX-Daily-Remaining. The per-minute limits are plan-based — see Rate Limits above, or call GET /v1/me for this credential's own figures.