usps-api and usps-webtools Are Dead: the 2026 Replacement
If your USPS integration stopped working and you are running any of these packages, the package is not the problem. The API behind it is gone.
USPS retired the Web Tools XML API at secure.shippingapis.com on January 25, 2026. Every one of these depended on that host:
| Package | Registry | Last published | Status |
|---|---|---|---|
usps-api |
PyPI | 0.5, July 2020 | Dead |
usps-webtools |
npm | 1.0.7, July 2021 | Dead |
usps-webtools-promise |
npm | 6.1.0, December 2023 | Dead |
All three still install cleanly and still see download traffic from developers who have not learned that the endpoint behind them no longer answers. Nothing in the package is broken. The install works, the import works, the API call does not.
The error you are staring at
# usps-api — broken since January 25, 2026
from usps import USPSApi
usps = USPSApi("YOUR_USER_ID")
address = usps.validate_address(
"1600 Pennsylvania Ave NW",
"Washington",
"DC",
)
# ConnectionError: HTTPSConnectionPool(host='secure.shippingapis.com')
# Max retries exceeded — the host is gone, not slow.// usps-webtools — broken since January 25, 2026
const USPS = require('usps-webtools');
const usps = new USPS({ userId: 'YOUR_USER_ID' });
usps.verify({
street1: '1600 Pennsylvania Ave NW',
city: 'Washington',
state: 'DC',
}, (err, address) => {
// Error: getaddrinfo ENOTFOUND secure.shippingapis.com
// Or: XML parse error on an empty response body.
});No retry count, timeout increase, or version pin changes this. There is no redirect and no compatibility shim.
Why dead packages keep getting installed
Three reasons, and the first one is the expensive one.
Lockfiles do not know the API died. CI resolves the dependency, the build goes green, the deploy succeeds. The USPS calls fail at runtime, in production, and unless you monitor USPS responses specifically, the first signal is a customer reporting that checkout will not accept their address.
The tutorials outrank the obituary. Search for USPS API integration in any language and results written years before the retirement still sit at the top, linking straight to the registry page. They rank because they accumulated backlinks while the API was alive.
Nobody published a deprecation notice. None of the three packages shipped a final release warning that the upstream was scheduled for retirement. The READMEs still describe a working integration.
The replacement
usps-v3 targets the current REST API at apis.usps.com. Same postal data, different protocol and different authentication.
# Python
pip uninstall usps-api
pip install usps-v3
# Node.js
npm uninstall usps-webtools usps-webtools-promise
npm install usps-v3| Old packages | usps-v3 |
|
|---|---|---|
| Host | secure.shippingapis.com (gone) |
apis.usps.com |
| Format | XML | JSON |
| Auth | User ID in the request | OAuth 2.0, handled by the SDK |
| Errors | Parse the XML error string | Typed exceptions |
| Maintained | No | Yes |
The credential changes shape. The old packages took a Web Tools User ID. The v3 API issues a Consumer Key and Consumer Secret against an app you register at developer.usps.com. Registering is free. Note that registering is not the same as being licensed to call the Addresses API, which is covered below.
Address validation, old and new
from usps_v3 import Client
# Or set USPS_CLIENT_ID and USPS_CLIENT_SECRET and call Client().
client = Client(client_id="your_client_id", client_secret="your_client_secret")
result = client.addresses.validate(
street_address="1600 Pennsylvania Ave NW",
city="Washington",
state="DC",
zip_code="20500",
)
# The Python SDK returns plain dictionaries, not objects.
print(result["address"]["streetAddress"]) # 1600 PENNSYLVANIA AVE NW
print(result["address"]["ZIPPlus4"]) # 0005import { USPSClient } from 'usps-v3';
const client = new USPSClient({
clientId: process.env.USPS_CLIENT_ID,
clientSecret: process.env.USPS_CLIENT_SECRET,
});
const result = await client.addresses.validate({
streetAddress: '1600 Pennsylvania Ave NW',
city: 'Washington',
state: 'DC',
ZIPCode: '20500', // capital ZIP — not zipCode
});
console.log(result.address.streetAddress); // 1600 PENNSYLVANIA AVE NWTwo details cost people an afternoon each. The Python SDK exports Client and returns dictionaries, so result["address"]["ZIPPlus4"] is right and result.address.zip_plus_4 is not. And the Node SDK takes ZIPCode with a capital ZIP, matching the USPS schema rather than the camelCase convention around it; passing zipCode sends nothing.
Tracking, old and new
info = client.tracking.track("9400111899223033005282")
print(info["statusCategory"]) # "Delivered"const tracking = await client.tracking.track('9400111899223033005282');
console.log(tracking.statusCategory); // 'Delivered'The method is track, on both. If you are porting from a guide that calls tracking.get(), that guide predates the current SDK.
What each operation is called now
| Operation | Web Tools XML | Python | Node.js |
|---|---|---|---|
| Validate address | AddressValidateRequest |
addresses.validate() |
addresses.validate() |
| City/state from ZIP | CityStateLookupRequest |
addresses.city_state() |
addresses.cityState() |
| Track a package | TrackFieldRequest |
tracking.track() |
tracking.track() |
| Domestic rates | RateV4Request |
prices.domestic() |
prices.domestic() |
| International rates | — | prices.international() |
prices.international() |
| Delivery estimates | — | standards.estimates() |
standards.estimates() |
| Drop-off locations | — | locations.dropoff() |
locations.dropoff() |
| Create a label | eVSRequest |
labels.create() |
labels.create() |
Errors you can actually catch
The largest practical gain over the old packages. Web Tools returned errors as XML strings you had to parse and pattern-match; the SDK raises typed exceptions.
from usps_v3 import Client, AuthError, ValidationError, RateLimitError, APIError
try:
result = client.addresses.validate(street_address="123 Main St")
except ValidationError as e:
print(f"Bad input on {e.field}: {e}")
except RateLimitError as e:
print(f"Rate limited — retry after {e.retry_after}s")
except AuthError as e:
print(f"Auth failed: {e}")
except APIError as e:
print(f"USPS error {e.status_code}: {e}")import { USPSClient, RateLimitError, ValidationError, AuthError } from 'usps-v3';
try {
await client.addresses.validate({ streetAddress: '' });
} catch (err) {
if (err instanceof ValidationError) {
console.log(`Bad field: ${err.field}`);
} else if (err instanceof RateLimitError) {
console.log(`Retry after ${err.retryAfter}s`);
} else if (err instanceof AuthError) {
console.log('Check credentials');
}
}RateLimitError is the one you will meet. USPS caps an application at 60 requests per hour across every endpoint, a limit Web Tools never enforced.
What is free now, and what is not
This is where guides written before August 2026 mislead.
Tracking, pricing, and service standards sit outside the USPS fee curve. Register an app, authenticate, call them.
Address validation does not. Since August 1, 2026 the Addresses API requires a signed license agreement and bills on a consumption-tier curve. That is a DocuSign round trip and a funded Enterprise Payment Account before your first validated address, not a checkbox. What the USPS API actually costs walks the curve and the enrollment order.
Labels need more than OAuth. Label creation requires a CRID, Mailer IDs, an Enterprise Payment Account, and COP claims linking. Both SDKs accept those as constructor arguments, but obtaining them is an enrollment project. The CRID and MID enrollment guide documents each step.
If you only need addresses standardized and geocoded rather than USPS-verified, there is a route with no license at all: RevAddress runs standardization, geocoding, and address extract against US Census Bureau data at no per-lookup cost. It will not return DPV or ZIP+4, because those are USPS data. For DPV, rates, and tracking, connect your own USPS license and the OAuth lifecycle, caching, and retries are handled on your credentials.
Token handling you no longer write
Both SDKs cache the OAuth token and refresh it about 30 minutes before the 8-hour expiry. Python also persists the token to ~/.usps-v3/tokens.json, which matters for short-lived processes: a cron job that runs every five minutes will reuse one token instead of requesting a new one per run, and token requests draw on the same 60-per-hour ceiling as everything else.
Start here
- USPS v3 migration guide for Node and Python — the full per-language walkthrough with a cutover playbook
- Web Tools to v3 migration guide — endpoint mapping and the raw OAuth flow if you are not using an SDK
- What the USPS API actually costs — the license and fee curve to plan before launch
- Get a free API key — Census-based standardization and geocoding, no USPS license required
Questions
- Is the usps-api Python package still working?
- No. usps-api targets the USPS Web Tools XML API, which was retired on January 25, 2026. The package installs and imports without complaint and then fails at runtime with a connection error. The replacement is pip install usps-v3, which targets the v3 REST API.
- What replaced usps-webtools and usps-webtools-promise?
- npm install usps-v3. Both legacy packages spoke the retired Web Tools XML API. The replacement covers the same address validation and tracking operations against the v3 REST API, with JSON responses, automatic OAuth token handling, and TypeScript definitions.
- Why did my build succeed if the package is dead?
- Because nothing about the package is broken. The registry entry, the install, and the import all work. What is gone is the API host it calls. The failure only appears when a request goes out, which is why lockfile-driven CI keeps shipping the dependency and the breakage surfaces as a customer report.
- Do I need new credentials to migrate?
- Yes. The old packages took a Web Tools User ID string. The v3 API uses OAuth 2.0, so you register an app at the USPS Developer Portal and use a Consumer Key and Consumer Secret. The SDK exchanges and refreshes tokens for you.
- Is address validation still free on the v3 API?
- No. Since August 1, 2026 the Addresses API requires a signed license agreement and bills on a consumption-tier curve. Tracking, pricing, and service standards sit outside that fee curve. Any guide that still calls v3 address validation free predates the change.
Read next
USPS Web Tools Shut Down on January 25, 2026: What Broke and the Fastest Way to the v3 API
USPS retired Web Tools on January 25, 2026. If ShippingAPI.dll, USERID, or secure.shippingapis.com is failing, map the XML route to v3 REST here.
10 min readMigration GuideUSPS v3 Migration Guide: Node.js and Python, XML to REST
Every Web Tools operation rewritten in the usps-v3 Python and Node SDKs, with the exact method names, the field that silently fails, and a cutover playbook.
15 min readMigration GuideUSPS Web Tools Migration: XML to v3 REST, End to End
Every Web Tools endpoint mapped to its v3 REST equivalent, the OAuth flow in three languages, the field swap that corrupts addresses, and the 60/hr wall.
12 min read