USPS Web Tools to v3 REST:
Complete Migration Guide
USPS is retiring the legacy XML Web Tools API. Here's everything you need to migrate to the new v3 REST API — endpoint mappings, OAuth 2.0 setup, rate limit reality, and working code samples in Python, Node.js, and PHP.
What's Changing
USPS is migrating from the legacy Web Tools XML API to a new RESTful v3 API. The key changes:
- XML → JSON — All requests and responses are now JSON. No more XML parsing.
- USERID → OAuth 2.0 — Client credentials grant flow replaces plain USERID in query strings.
- Rate limits — Direct USPS v3 access is capped at 60 requests/hour (not per minute).
- New base URL —
https://apis.usps.com(production),https://apis-tem.usps.com(testing).
Endpoint Mapping
Here's how the most-used Web Tools endpoints map to v3 REST:
| Legacy (XML) | v3 REST |
|---|---|
| Verify (AddressValidateRequest) | GET /addresses/v3/validate |
| CityStateLookup | GET /addresses/v3/city-state |
| ZipCodeLookup | GET /addresses/v3/zipcode |
| TrackV2 (TrackFieldRequest) | GET /tracking/v3/tracking/{id} |
| RateV4 | POST /prices/v3/total-rates/search |
| eVS (CreateLabel) | POST /labels/v3/label |
OAuth 2.0 Setup
The v3 API uses OAuth 2.0 client credentials grant. Tokens are valid for 8 hours. Here's how to get one:
import httpx
# Exchange client credentials for an access token
resp = httpx.post(
"https://apis.usps.com/oauth2/v3/token",
data={
"grant_type": "client_credentials",
"client_id": YOUR_CLIENT_ID,
"client_secret": YOUR_CLIENT_SECRET,
"scope": "addresses tracking prices labels",
},
)
token = resp.json()["access_token"]
# Token valid for 8 hours (28800 seconds) const resp = await fetch(
"https://apis.usps.com/oauth2/v3/token",
{
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: YOUR_CLIENT_ID,
client_secret: YOUR_CLIENT_SECRET,
scope: "addresses tracking prices labels",
}),
}
);
const { access_token } = await resp.json();
// Token valid for 8 hours // PHP — OAuth Token Request
$ch = curl_init('https://apis.usps.com/oauth2/v3/token');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'],
CURLOPT_POSTFIELDS => http_build_query([
'grant_type' => 'client_credentials',
'client_id' => $clientId,
'client_secret' => $clientSecret,
'scope' => 'addresses tracking prices labels',
]),
]);
$token = json_decode(curl_exec($ch))->access_token;
// Token valid for 8 hours The Rate Limit Problem
The biggest gotcha in the v3 migration: USPS caps direct API access at 60 requests per hour. That's 1 request per minute. For context, a single WooCommerce store doing 50 orders/day needs at minimum 100+ API calls (address validation + rate shopping + label creation).
Strategies to handle this:
- 1. Cache aggressively — Address validation results don't change. Cache validated addresses for 30 days.
- 2. Batch operations — Group tracking lookups and price checks into batch requests.
- 3. Request a rate limit increase — Contact USPS via emailus.usps.com. Explain your volume. They can increase to 300-5000 req/hr.
- 4. Use a managed API — Services like RevAddress handle rate limiting, caching, and token management for you.
Before & After: Address Validation
Here's what a complete migration looks like for the most common endpoint — address validation:
POST https://secure.shippingapis.com
/ShippingAPI.dll?API=Verify
<AddressValidateRequest
USERID="YOUR_USERID">
<Address>
<Address1/>
<Address2>
1600 Pennsylvania Ave
</Address2>
<City>Washington</City>
<State>DC</State>
<Zip5>20500</Zip5>
<Zip4/>
</Address>
</AddressValidateRequest> GET https://apis.usps.com
/addresses/v3/validate
?streetAddress=
1600+Pennsylvania+Ave
&city=Washington
&state=DC
&ZIPCode=20500
Authorization: Bearer {token}
// Clean JSON response
{
"address": {
"streetAddress":
"1600 PENNSYLVANIA AVE NW",
"city": "WASHINGTON",
"state": "DC"
}
} Migration Timeline
USPS hasn't announced a hard cutoff date for Web Tools, but the signals are clear:
- 2024 v3 REST API launched. New developer registrations redirected away from Web Tools.
- 2025 Web Tools documentation moved to legacy section. Rate limit enforcement tightened.
- 2026 "API Access Control" initiative launching April 2026. Expected further restrictions on legacy endpoints.
Don't wait for the shutdown notice. Migrate now while you can test against both APIs in parallel.
Official SDKs — The Fastest Migration Path
If you want to skip the raw HTTP calls entirely, use the official open-source USPS v3 SDKs. Zero dependencies in Node.js, one dependency (httpx) in Python. Full TypeScript types included.
# Install the SDK
pip install usps-v3
# Validate an address in 3 lines
from usps_v3 import USPSClient
client = USPSClient(
client_id="your_client_id",
client_secret="your_client_secret",
)
result = client.addresses.validate(
street_address="1600 Pennsylvania Ave NW",
city="Washington",
state="DC",
)
print(result) # Standardized address + DPV // Install the SDK
npm install usps-v3
// Validate an address in 3 lines
import { USPSClient } from 'usps-v3';
const client = new USPSClient({
clientId: 'your_client_id',
clientSecret: 'your_client_secret',
});
const result = await client.addresses.validate({
streetAddress: '1600 Pennsylvania Ave NW',
city: 'Washington',
state: 'DC',
});
console.log(result); // Standardized address + DPV Both SDKs handle OAuth token management automatically — tokens are cached in memory and refreshed 30 minutes before expiry. Source code: Python · Node.js
Skip the migration headaches
RevAddress handles OAuth, rate limits, caching, and retries. Get a REST API key and start shipping in 5 minutes.
Get API Key →