USPS Web Tools Migration Checklist (2026): Every Step, In Order
USPS Web Tools XML API stopped working January 25, 2026. If you’re reading this, you either migrated already or something broke. This checklist covers every step of the migration from Web Tools to the USPS v3 REST API — whether you go direct or through RevAddress.
Print this. Check the boxes. Ship it.
Pre-migration audit (before you write any code)
1. Inventory your Web Tools endpoints. Open your codebase and grep for these:
| Old Web Tools Endpoint | v3 REST Replacement |
|---|---|
AddressValidateRequest | GET /addresses/v3/address |
ZipCodeLookupRequest | GET /addresses/v3/city-state |
CityStateLookupRequest | GET /addresses/v3/city-state |
RateV4Request | GET /domestic-prices/v3/base-rates |
IntlRateV2Request | GET /international-prices/v3/base-rates |
TrackV2Request / TrackFieldRequest | GET /tracking/v3/tracking/{trackingNumber} |
Verify (eVS) | POST /labels/v3/label |
DelivConfirmCertifyV4 | POST /labels/v3/label |
SDCGetLocationsRequest | GET /locations/v3/post-office |
FirstClassMailRequest | GET /service-standards/v3/estimates |
2. Count your daily API calls. This determines your tier:
| Daily volume | Recommended path |
|---|---|
| Under 50 | USPS direct (free, 60 req/hr is enough) |
| 50–200 | RevAddress Free tier (1K req/mo) |
| 200–1,000 | RevAddress Starter ($29/mo, 5K req/mo) |
| 1,000–5,000 | RevAddress Growth ($79/mo, 25K req/mo) |
| 5,000+ | RevAddress Pro ($199/mo) or BYOK |
3. Check your dependencies. These npm/pip packages are dead:
| Dead Package | Replacement |
|---|---|
usps-webtools (npm) | usps-v3 (npm) |
usps-api (pip) | usps-v3 (pip) |
usps (pip) | usps-v3 (pip) |
| Any XML parser for USPS | Not needed — v3 returns JSON |
Phase 1: Authentication (the biggest change)
Web Tools used a USERID in the query string. v3 uses OAuth 2.0 with client credentials.
If going direct with USPS:
- Register at developers.usps.com
- Create an application — you’ll get a Consumer Key and Consumer Secret
- Complete CRID/MID enrollment at gateway.usps.com (required for labels)
- Implement OAuth token refresh — tokens expire every 4 hours
- Handle the April 2026 Access Control changes (CRID/MID validation on every call)
If using RevAddress:
- Sign up at revaddress.com/signup — 30 seconds
- Get your API key (
rv_test_*for sandbox,rv_live_*for production) - Add
X-API-Keyheader to your requests - Done — no OAuth, no CRID enrollment, no token refresh
# Web Tools: USERID in XML body
import requests
import xml.etree.ElementTree as ET
xml = f"""<AddressValidateRequest USERID="{USERID}">
<Address>
<Address1></Address1>
<Address2>1600 Pennsylvania Ave NW</Address2>
<City>Washington</City>
<State>DC</State>
<Zip5>20500</Zip5>
</Address>
</AddressValidateRequest>"""
resp = requests.get(
"https://secure.shippingapis.com/ShippingAPI.dll",
params={"API": "Verify", "XML": xml}
)
root = ET.fromstring(resp.text)
city = root.find(".//City").text Phase 2: Endpoint migration (one at a time)
Migrate one endpoint, test it, deploy it, then move to the next. Don’t batch.
Address validation
The field names changed. Web Tools used Address2 for street (yes, really). v3 uses streetAddress.
| Web Tools Field | v3 Field |
|---|---|
Address1 (secondary) | secondaryAddress |
Address2 (street) | streetAddress |
City | city |
State | state |
Zip5 | ZIPCode |
Zip4 | (returned in response, not sent) |
Rate lookups
Web Tools RateV4Request becomes GET /domestic-prices/v3/base-rates:
<RateV4Request USERID="xxx">
<Package ID="1ST">
<Service>PRIORITY</Service>
<ZipOrigination>10001</ZipOrigination>
<ZipDestination>90210</ZipDestination>
<Pounds>2</Pounds>
<Ounces>0</Ounces>
<Container>RECTANGULAR</Container>
<Width>6</Width>
<Length>12</Length>
<Height>4</Height>
</Package>
</RateV4Request> Tracking
Web Tools TrackV2 / TrackFieldRequest becomes a simple GET:
curl "https://api.revaddress.com/api/tracking/9400111899223100001234" \
-H "X-API-Key: rv_live_your_key_here" Phase 3: Error handling
Web Tools returned errors as XML with inconsistent structures. v3 returns structured JSON errors:
{
"apiVersion": "v3",
"error": {
"code": "ADDRESS_NOT_FOUND",
"message": "Address Not Found.",
"source": "USPS"
}
} Map your existing error handling:
| Web Tools Error | v3 Error Code | Action |
|---|---|---|
<Error><Number>-2147219401</Number> | ADDRESS_NOT_FOUND | Address doesn’t exist in USPS database |
<Error><Number>80040B19</Number> | INVALID_REQUEST | Missing required field |
Authorization failure | 401 Unauthorized | Token expired (direct) or invalid key (RevAddress) |
| Empty response / timeout | 503 Service Unavailable | USPS is down (rare) — implement retry |
Phase 4: Testing checklist
Run these before deploying:
- Address validation returns
DPVConfirmation: "Y"for known good address (1600 Pennsylvania Ave NW, Washington DC 20500) - Address validation returns
DPVConfirmation: "N"for known bad address (123 Fake Street, Nowhere, XX 00000) - Rate lookup returns pricing for USPS Ground Advantage between two valid ZIPs
- Tracking returns status for a known tracking number
- Error response is caught and handled (not a crash)
- Rate limiting is handled (429 or rate limit exceeded)
- If using labels: test label creation in sandbox (rv_test_ key) before production
Phase 5: Deploy and verify
- Remove old Web Tools USERID from environment variables
- Remove XML parsing dependencies (if no longer needed)
- Update API endpoint URLs in configuration
- Deploy to staging, run full test suite
- Monitor error rates for 24 hours after production deploy
- Remove old
shippingapis.comURLs from any allowlists/firewalls
Common migration pitfalls
“My addresses are coming back different.” USPS v3 returns standardized addresses (all caps, ZIP+4 appended). If your UI displays raw API responses, users will see “1600 PENNSYLVANIA AVE NW” instead of “1600 Pennsylvania Ave NW”. Title-case it on your end.
“I’m getting rate limited.” USPS allows 60 requests per hour on the free tier. If you’re doing checkout validation, that’s one customer per minute. Use RevAddress (120+ req/min on Starter) or implement caching — most addresses don’t change.
“My tracking integration broke.” Web Tools returned tracking as XML with deeply nested elements. v3 returns flat JSON. If you’re parsing tracking data, you need to update every field accessor.
“I need both USPS and FedEx/UPS.” RevAddress is USPS-only. If you need multi-carrier, consider using RevAddress for USPS (where it’s cheapest) and a separate integration for other carriers.
Timeline context
| Date | What happened |
|---|---|
| Jan 25, 2026 | USPS Web Tools XML API shut down |
| Mar 5, 2026 | USPS v3 REST API fully stable (rate limits settled) |
| Mar 17, 2026 | EasyPost free tier changes — must select plan or lose labels |
| Apr 2026 | USPS Access Control tightening — CRID/MID required on all calls |
Related
- USPS Web Tools Endpoint Mapping — full endpoint-by-endpoint reference
- USPS Web Tools Shutdown — what happened and why
- EasyPost March 17 Deadline — your options before the cutoff
- USPS v3 Address Validation Quickstart — working code in 30 seconds
- Full API Reference — all 41 endpoints
Ready to migrate?
293 tests. 41 routes. USPS-approved. Flat monthly pricing.