Skip to content
All posts

USPS Web Tools to v3 REST API: Complete Endpoint Mapping

Updated 12 min read By RevAddress

Topics: Web Tools migration, USPS v3, Reference

USPS retired the Web Tools XML API on January 25, 2026. Every legacy XML surface now has a REST equivalent on a different host, with a different auth model and, in several places, different field semantics.

Every route below is checked against the published USPS v3 OpenAPI specifications.

Master endpoint mapping

Address APIs

Web Tools XML API v3 REST endpoint Method Notes
AddressValidateRequest /addresses/v3/address GET Query params, not an XML body. DPV comes back in additionalInfo
CityStateLookupRequest /addresses/v3/city-state GET ZIP to city and state
ZipCodeLookupRequest /addresses/v3/zipcode GET Returns ZIP+4

Tracking APIs

Web Tools XML API v3 REST endpoint Method Notes
TrackFieldRequest /tracking/v3/tracking/{trackingNumber} GET Tracking number is a path parameter now
TrackConfirmRequest /tracking/v3/tracking/{trackingNumber} GET Unified. Same route as TrackField

Shipping and label APIs

Web Tools XML API v3 REST endpoint Method Notes
eVS (Express Mail) /labels/v3/label POST Needs a Payment Authorization token
eVS (Priority Mail) /labels/v3/label POST Same route, different mailClass
eVS (Ground Advantage) /labels/v3/label POST Replaces First-Class Package Service
eVSCancel /labels/v3/label/{trackingNumber} DELETE Cancel or refund. See the note below

Pricing APIs

Web Tools XML API v3 REST endpoint Method Notes
RateV4Request /prices/v3/base-rates/search POST JSON body replaces the XML envelope. One mail class per call
IntlRateV2Request /international-prices/v3/base-rates/search POST Same shape, ISO alpha-2 country codes

The base path is prices, not domestic-prices, and the operation is a POST to a /search route rather than a GET. /prices/v3/total-rates/search is the sibling route when you want totals including extra services rather than base rates alone.

Service standards and locations

Web Tools XML API v3 REST endpoint Method Notes
SDCGetLocationsRequest /service-standards/v3/estimates GET Delivery estimates by origin and destination
POLocatorRequest /locations/v3/post-office-locations GET Post offices by ZIP or coordinates

The locations route is post-office-locations, not post-offices. The same API also serves /locations/v3/dropoff-locations and /locations/v3/parcel-locker-locations, which have no Web Tools equivalent at all.

Authentication from USERID to OAuth 2.0

Web Tools embedded a plain-text USERID in every XML request body. No scopes, no expiry, no rotation. The v3 API uses OAuth 2.0 client credentials, with tokens that last 8 hours.

Before: Web Tools auth XML
POST https://secure.shippingapis.com/ShippingAPI.dll?API=Verify

<AddressValidateRequest USERID="YOUR_USERID">
<Address>
  <Address1>Apt 2</Address1>
  <Address2>1600 Pennsylvania Ave</Address2>
</Address>
</AddressValidateRequest>

<!-- USERID in plain text. No expiry. No scopes. No rotation. -->
After: OAuth 2.0 Bash
# Step 1: get a token (8-hour lifetime, expires_in 28800)
curl -X POST "https://apis.usps.com/oauth2/v3/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"

# Step 2: send it as a Bearer token
curl "https://apis.usps.com/addresses/v3/address?streetAddress=1600+Pennsylvania+Ave+NW&city=Washington&state=DC&ZIPCode=20500" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Cache that token. A token request spends one call against the same 60-per-hour budget as the address lookup it authorizes, so refetching per call halves your throughput.

Eight behavior changes from Web Tools to v3

01. The rate limit is 60 an hour, and it is shared

Web Tools had no meaningful documented limit. The v3 API allows 60 requests an hour per application, and address validation, tracking, pricing, and labels all draw on the same window. Token fetches count too. You can request an increase and build for the default from day one.

02. Label creation needs two tokens, in two different headers

Labels are the only endpoint family that needs a second authorization token: the standard OAuth Bearer token plus a Payment Authorization token tied to your CRID and MID.

The two-token flow Bash
# 1. Standard OAuth token
POST https://apis.usps.com/oauth2/v3/token
   grant_type=client_credentials

# 2. Payment Authorization token
POST https://apis.usps.com/payments/v3/payment-authorization
   Authorization: Bearer {standard_token}
   {
     "roles": [
       { "roleName": "PAYER",
         "CRID": "YOUR_CRID",
         "MID": "YOUR_MID",
         "accountType": "EPS",
         "accountNumber": "YOUR_EPS_ACCOUNT" }
     ]
   }

# 3. Create the label with BOTH, in separate headers
POST https://apis.usps.com/labels/v3/label
   Authorization: Bearer {standard_token}
   X-Payment-Authorization-Token: {payment_token}

Two details in that flow carry exact names. The payment endpoint is /payments/v3/payment-authorization, not a payment-authorization/v3/payment-token route. And the header is X-Payment-Authorization-Token, not X-Payment-Authorization. The USPS Labels specification marks that header required on label creation, reprint, and cancellation. Put the token in the JSON body instead and the header is absent, which USPS reports as an authorization failure that reads like a credential problem when your credentials are fine.

03. Address1 and Address2 semantics are reversed

Field mapping: do not get this wrong JSON
// Web Tools XML (reversed semantics)
// <Address1>Apt 2</Address1>              <- secondary
// <Address2>1600 Penn Ave</Address2>      <- primary street

// v3 REST JSON (plain semantics)
{
"streetAddress": "1600 Penn Ave",
"secondaryAddress": "Apt 2"
}

// Map Address1 -> streetAddress and every apartment
// number becomes the street. It fails silently.

04. DPV data moved into additionalInfo

The field is still called DPVConfirmation. What changed is where it sits: the v3 response nests it inside an additionalInfo object alongside deliveryPoint, carrierRoute, countyName, congressDistrict, footnotes, vacant, business, and DPVCMRA, rather than putting it beside the standardized address lines. Code that reaches for it at the top level of the address object finds nothing and reads it as “not confirmed.”

The codes, per the USPS specification: Y both primary and secondary confirmed, D primary confirmed with secondary missing, S primary confirmed with secondary present but unconfirmed, N neither confirmed.

05. Cancel and refund are different operations

DELETE /labels/v3/label/{trackingNumber} cancels a label while no Shipping Services File has been created for it. Once one exists, the same call becomes a refund request and returns a disputeId you use to track the outcome. USPS issues one unique disputeId per CRID per day, so resubmitting the same label the same day is rejected rather than queued.

The trigger is the Shipping Services File, not a 24-hour clock.

06. New base URLs

Environment Web Tools v3 REST
Production secure.shippingapis.com apis.usps.com
Testing stg-secure.shippingapis.com apis-tem.usps.com

The test environment takes your production credentials rather than a separate sandbox key pair, and the same product entitlements apply there. A flow that fails in TEM for lack of a granted product will fail identically in production.

07. One endpoint became many routes

Web Tools used an API name per operation (?API=Verify, ?API=CityStateLookup) but every one of them hit the same ShippingAPI.dll. The v3 API gives each resource its own path. Do not port the old dispatcher; map each operation to its dedicated route.

08. The Addresses API license since August 1, 2026

This one post-dates the shutdown by six months. Since August 1, 2026 the Addresses API requires a signed license agreement, and USPS states that “customers who did not complete onboarding no longer have access to the Addresses API.” Until it is linked, a correctly mapped, correctly authenticated v3 integration returns 401 on every address call. The license is requested through the Business Portal at cop.usps.com under My Account → API Licenses → Add an Addresses API License, signed by DocuSign, and countersigned by USPS on no published schedule. The enrollment guide walks it end to end.

Field name changes from XML to JSON

Web Tools XML field v3 REST JSON field Change
Address1 secondaryAddress Semantics reversed
Address2 streetAddress Semantics reversed
Zip5 ZIPCode Capital ZIP
Zip4 ZIPPlus4 Capital ZIP, Plus4 suffix
FirmName firm Simplified
DPVConfirmation DPVConfirmation, inside additionalInfo Same name, moved one level down
USERID attribute Authorization header Not in the body anymore

The machinery RevAddress runs on your USPS credentials

The mapping is mechanical; the 60-per-hour ceiling, the two-token label flow, and the license take infrastructure and enrollment.

RevAddress runs the machinery against your own USPS credentials: the OAuth lifecycle, the X-Payment-Authorization-Token header, Retry-After handling, caching, and retries, with credentials encrypted at rest. DPV, ZIP+4, rates, tracking, and labels are USPS data and bill on your license, on every plan. Census-based standardization, geocoding, and address extract need no USPS license at all.

Start here

Questions

What replaced AddressValidateRequest in the USPS v3 API?
GET /addresses/v3/address on the apis.usps.com host. It takes query parameters instead of an XML body, returns JSON, and carries DPV data in the additionalInfo object of the response rather than requiring a separate call.
What replaced RateV4Request?
POST /prices/v3/base-rates/search, with international pricing at POST /international-prices/v3/base-rates/search. Note the base path is prices, not domestic-prices, and the operation is a POST to a search route rather than a GET.
How does label creation authorize in v3?
With two tokens. The standard OAuth Bearer token goes in the Authorization header, and a Payment Authorization token from POST /payments/v3/payment-authorization goes in the X-Payment-Authorization-Token header. Sending the second one in the JSON body returns a 401 on a label call.
Can I still cancel a USPS label after creating it?
A label can be canceled while no Shipping Services File has been created for it. Once one exists, you submit a refund request instead, which returns a disputeId for tracking. USPS issues one unique disputeId per CRID per day, so duplicate same-day submissions for the same label are rejected.
Are Address1 and Address2 really reversed?
Yes. In Web Tools XML, Address1 was the secondary line such as an apartment or suite, and Address2 was the primary street address. In v3, streetAddress is primary and secondaryAddress is secondary. Mapping Address1 to streetAddress turns every apartment number into a street.