Skip to content
· 10 min read · Migration Guide

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:

Python — usps-api (broken)
# 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
Node.js — usps-webtools (broken)
// 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.

Migration (30 seconds)
# 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

Before — usps-api (broken)
# 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
After — usps-v3 (works)
# 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

Before — usps-webtools (broken)
// 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
});
After — usps-v3 (works)
// 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.

Python — Old (broken)
# OLD: usps-api tracking — DEAD
from usps import USPSApi

usps = USPSApi("YOUR_USER_ID")
tracking = usps.track("9400111899223456789012")
# ConnectionError: secure.shippingapis.com is gone
Node.js — Old (broken)
// 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
});
Python — New (works)
# 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}")
Node.js — New (works)
// 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-api release 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. 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. 2 Swap the package. pip install usps-v3 or npm install usps-v3. Remove the old package from your lockfile.
  3. 3 Update imports. Python: from usps_v3 import USPSClient. Node: import { USPSClient } from 'usps-v3'.
  4. 4 Replace User ID with OAuth credentials. The old userId parameter is gone. Pass client_id and client_secret to the client constructor. Tokens are managed automatically.
  5. 5 Update method calls. Use the code examples above. The parameter names changed slightly (street_address in Python, streetAddress in Node), but the data is the same.
  6. 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

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