Skip to content
All Posts
Migration

USPS Web Tools Migration Checklist (2026): Every Step, In Order

· 10 min read

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 Endpointv3 REST Replacement
AddressValidateRequestGET /addresses/v3/address
ZipCodeLookupRequestGET /addresses/v3/city-state
CityStateLookupRequestGET /addresses/v3/city-state
RateV4RequestGET /domestic-prices/v3/base-rates
IntlRateV2RequestGET /international-prices/v3/base-rates
TrackV2Request / TrackFieldRequestGET /tracking/v3/tracking/{trackingNumber}
Verify (eVS)POST /labels/v3/label
DelivConfirmCertifyV4POST /labels/v3/label
SDCGetLocationsRequestGET /locations/v3/post-office
FirstClassMailRequestGET /service-standards/v3/estimates

2. Count your daily API calls. This determines your tier:

Daily volumeRecommended path
Under 50USPS direct (free, 60 req/hr is enough)
50–200RevAddress Free tier (1K req/mo)
200–1,000RevAddress Starter ($29/mo, 5K req/mo)
1,000–5,000RevAddress 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 PackageReplacement
usps-webtools (npm)usps-v3 (npm)
usps-api (pip)usps-v3 (pip)
usps (pip)usps-v3 (pip)
Any XML parser for USPSNot 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:

  1. Register at developers.usps.com
  2. Create an application — you’ll get a Consumer Key and Consumer Secret
  3. Complete CRID/MID enrollment at gateway.usps.com (required for labels)
  4. Implement OAuth token refresh — tokens expire every 4 hours
  5. Handle the April 2026 Access Control changes (CRID/MID validation on every call)

If using RevAddress:

  1. Sign up at revaddress.com/signup — 30 seconds
  2. Get your API key (rv_test_* for sandbox, rv_live_* for production)
  3. Add X-API-Key header to your requests
  4. Done — no OAuth, no CRID enrollment, no token refresh
Authentication — before and after
# 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 Fieldv3 Field
Address1 (secondary)secondaryAddress
Address2 (street)streetAddress
Citycity
Statestate
Zip5ZIPCode
Zip4(returned in response, not sent)

Rate lookups

Web Tools RateV4Request becomes GET /domestic-prices/v3/base-rates:

Rate lookup
<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:

Package tracking
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:

v3 error response
{
"apiVersion": "v3",
"error": {
  "code": "ADDRESS_NOT_FOUND",
  "message": "Address Not Found.",
  "source": "USPS"
}
}

Map your existing error handling:

Web Tools Errorv3 Error CodeAction
<Error><Number>-2147219401</Number>ADDRESS_NOT_FOUNDAddress doesn’t exist in USPS database
<Error><Number>80040B19</Number>INVALID_REQUESTMissing required field
Authorization failure401 UnauthorizedToken expired (direct) or invalid key (RevAddress)
Empty response / timeout503 Service UnavailableUSPS 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.com URLs 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

DateWhat happened
Jan 25, 2026USPS Web Tools XML API shut down
Mar 5, 2026USPS v3 REST API fully stable (rate limits settled)
Mar 17, 2026EasyPost free tier changes — must select plan or lose labels
Apr 2026USPS Access Control tightening — CRID/MID required on all calls

Ready to migrate?

293 tests. 41 routes. USPS-approved. Flat monthly pricing.

3 SDKs (Python · Node · PHP) MIT Licensed No per-label fees