usps-api / usps-webtools Are Dead
Here’s the 2026 Replacement
The usps-api PyPI package (13,725 downloads/month), usps-webtools (5,384/mo), and usps-webtools-promise (6,404/mo) all target the USPS Web Tools XML API — which was permanently shut down on January 25, 2026. If you are still using them, your integration is broken right now. The replacement is one command away.
What Happened
On January 25, 2026, USPS shut down the Web Tools XML API at secure.shippingapis.com. This was the endpoint that every one of these packages depended on. The domain no longer resolves. There is no redirect. Every API call through these packages now fails:
| Package | Registry | Downloads/mo | Last Updated | Status |
|---|---|---|---|---|
| usps-api | PyPI | 13,725 | July 2020 | Dead |
| usps-webtools | npm | 5,384 | 2018 | Dead |
| usps-webtools-promise | npm | 6,404 | 2019 | Dead |
Combined, these packages still pull 25,500+ downloads every month from developers who don't realize the underlying API is gone. The packages install fine. They import fine. They just fail at runtime.
The Error You’re Seeing
If you are using any of these packages, you are hitting one of these errors right now:
# 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 — USPS Web Tools is GONE // 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 — empty response body
});
The host secure.shippingapis.com is permanently offline. No amount of retries, timeout increases, or version pinning will fix this. The API itself is gone.
The Fix: One Command
The replacement is the usps-v3 SDK, which targets the new USPS v3 REST API at apis.usps.com. Same USPS data, same endpoints — just REST + JSON instead of SOAP + XML.
# Python migration (30 seconds)
pip uninstall usps-api
pip install usps-v3
# Node.js migration (30 seconds)
npm uninstall usps-webtools usps-webtools-promise
npm install usps-v3 | Old Packages | usps-v3 | |
|---|---|---|
| API Target | secure.shippingapis.com (dead) | apis.usps.com (live) |
| Data Format | SOAP + XML | REST + JSON |
| Authentication | User ID in query string | OAuth 2.0 (automatic) |
| Typed Responses | No | Yes |
| Error Handling | Parse XML error strings | Typed exceptions (401, 429, etc.) |
| Maintained | No (2018–2020) | Yes (2026) |
Credentials change: The old packages used a USPS User ID passed in the query string. The new API uses OAuth 2.0 with a Client ID and Client Secret from developer.usps.com. Registration is free. The SDK manages tokens automatically.
Address Validation: Old vs New
Address validation was the most common use of usps-api and usps-webtools. Here is the side-by-side migration for both languages. The new code is cleaner, typed, and actually works.
Python: usps-api → usps-v3
# 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 — USPS Web Tools is GONE # usps-v3 — the 2026 replacement
from usps_v3 import USPSClient # also: from usps_v3 import Client
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",
zip_code="20500",
)
print(result.address.street_address) # 1600 PENNSYLVANIA AVE NW
print(result.address.city) # WASHINGTON
print(result.address.zip_plus_4) # 0005
print(result.address.dpv_confirmation) # Y = confirmed deliverable Node.js: usps-webtools → usps-v3
// 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 — empty response body
}); // usps-v3 — the 2026 replacement
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',
zipCode: '20500',
});
console.log(result.address.streetAddress); // 1600 PENNSYLVANIA AVE NW
console.log(result.address.city); // WASHINGTON
console.log(result.address.zipPlus4); // 0005
console.log(result.address.dpvConfirmation); // Y = confirmed deliverable Key differences: The old packages used a User ID string. The new SDK uses OAuth Client ID + Client Secret (managed automatically). Python uses street_address (snake_case). Node uses streetAddress (camelCase). Both SDKs return typed response objects — no more parsing raw XML or JSON dictionaries.
Package Tracking: Old vs New
Tracking was the second most common use case. The old packages returned XML that you had to parse yourself. The new SDK returns typed objects with status, delivery date, and full event history.
# OLD: usps-api tracking — DEAD
from usps import USPSApi
usps = USPSApi("YOUR_USER_ID")
tracking = usps.track("9400111899223456789012")
# ConnectionError: secure.shippingapis.com is gone // OLD: usps-webtools tracking — DEAD
const USPS = require('usps-webtools');
const usps = new USPS({ userId: 'YOUR_USER_ID' });
usps.track('9400111899223456789012', (err, result) => {
// Error: ENOTFOUND secure.shippingapis.com
}); # NEW: usps-v3 tracking — works
from usps_v3 import USPSClient
client = USPSClient(
client_id="your_client_id",
client_secret="your_client_secret",
)
tracking = client.tracking.get(
tracking_number="9400111899223456789012",
)
print(tracking.status) # "Delivered"
print(tracking.status_category) # "Delivered"
print(tracking.delivery_date) # "2026-03-08"
for event in tracking.events:
print(f"{event.date} | {event.city}, {event.state} | {event.description}") // NEW: usps-v3 tracking — works
import { USPSClient } from 'usps-v3';
const client = new USPSClient({
clientId: 'your_client_id',
clientSecret: 'your_client_secret',
});
const tracking = await client.tracking.get({
trackingNumber: '9400111899223456789012',
});
console.log(tracking.status); // "Delivered"
console.log(tracking.statusCategory); // "Delivered"
console.log(tracking.deliveryDate); // "2026-03-08"
for (const event of tracking.events) {
console.log(`${event.date} | ${event.city}, ${event.state} | ${event.description}`);
} Free tier: Both address validation and package tracking are completely free on the USPS v3 API. These are the two most common use cases from the old packages. No credit card, no business account — register at developer.usps.com and start calling.
Why 25,500 Devs/Month Are Still Installing Dead Packages
Three reasons:
- CI/CD pipelines. Automated builds keep pulling these packages from lockfiles. The install succeeds. The app deploys. The USPS calls silently fail at runtime — and if you aren't monitoring USPS responses specifically, you don't notice until a customer reports broken tracking or address validation.
- Outdated tutorials. Google "usps api python" and the top results still reference
usps-api. Stack Overflow answers from 2021 link directly to the PyPI page. These results rank because they accumulated years of backlinks before the API died. - No deprecation notice. None of these packages were updated with a deprecation warning. The last
usps-apirelease was July 2020 — five and a half years before the API shut down. The README still says "working" with no mention of the retirement.
What Changed Under the Hood
The USPS replaced its entire API stack. Same postal data, completely different protocol:
| Web Tools (Dead) | v3 REST API (Current) | |
|---|---|---|
| Base URL | secure.shippingapis.com | apis.usps.com |
| Protocol | SOAP / XML | REST / JSON |
| Auth | User ID (query param) | OAuth 2.0 Bearer token |
| Addresses | AddressValidateRequest | GET /addresses/v3/address |
| Tracking | TrackV2Request | GET /tracking/v3/tracking/{trackingNumber} |
| Rates | RateV4Request | POST /prices/v3/total-rates/search |
| Rate Limit | Undocumented | 60 req/hr (documented, typed errors) |
5-Minute Migration Checklist
Complete migration from any of these dead packages:
- 1 Register at USPS Developer Portal. Go to developer.usps.com, create an account, and create an application. Copy your Client ID and Client Secret. Takes 2 minutes. Free.
- 2 Swap the package.
pip install usps-v3ornpm install usps-v3. Remove the old package from your lockfile. - 3 Update imports. Python:
from usps_v3 import USPSClient. Node:import { USPSClient } from 'usps-v3'. - 4 Replace User ID with OAuth credentials. The old
userIdparameter is gone. Passclient_idandclient_secretto the client constructor. Tokens are managed automatically. - 5 Update method calls. Use the code examples above. The parameter names changed slightly (
street_addressin Python,streetAddressin Node), but the data is the same. - 6 Deploy. That is it. Same USPS data, better developer experience, and it actually works.
usps-v3 SDK Details
Both SDKs are MIT licensed, open source, and cover the same API surface. Pick your language:
| Feature | Python usps-v3 | Node.js usps-v3 |
|---|---|---|
| Install | pip install usps-v3 | npm install usps-v3 |
| Exports | Client + USPSClient | USPSClient |
| Address Validation | Free | Free |
| Package Tracking | Free | Free |
| Auto OAuth Tokens | Yes | Yes |
| Typed Responses | dataclasses + type hints | Full TypeScript definitions |
| Typed Exceptions | Yes | Yes |
| Dependencies | 1 (httpx) | 0 |
| Min Runtime | Python 3.8+ | Node.js 18+ |
| License | MIT | MIT |
Further Reading
- Python Quickstart Guide — Full error handling, retry patterns, rate shopping, and DPV reference table.
- Node.js Quickstart Guide — TypeScript usage, label creation, async patterns, and full error hierarchy.
- Full Migration Guide — Complete endpoint mapping from every USPS Web Tools API to v3 REST equivalents.
- Endpoint Mapping Reference — Every Web Tools XML endpoint mapped to its v3 REST equivalent.
- OAuth Troubleshooting — If you hit 401 errors during migration, every cause and fix is documented here.
- Source Code on GitHub — MIT licensed. Issues and contributions welcome.
Need more than 60 req/hr?
RevAddress provides a managed USPS v3 API with 600 req/min, built-in caching, automatic token management, and BYOK support. Same usps-v3 SDK — just point it at RevAddress.
Related Articles
Python Quickstart: usps-v3 SDK
Install and start validating addresses, tracking packages, and comparing rates.
Node.js Quickstart: usps-v3 SDK
Full TypeScript support with automatic OAuth token management.
Python vs Node.js SDK: Feature Comparison
Side-by-side comparison of both SDKs with code examples.