PHPackages                             johnpaulmedina/laravel-usps - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Validation &amp; Sanitization](/categories/validation)
4. /
5. johnpaulmedina/laravel-usps

ActiveLibrary[Validation &amp; Sanitization](/categories/validation)

johnpaulmedina/laravel-usps
===========================

USPS Addresses API v3 (OAuth2) for Laravel

v13.4.1(4mo ago)3623.3k↑221.4%32[2 PRs](https://github.com/johnpaulmedina/laravel-usps/pulls)MITPHPPHP ^8.1CI passing

Since Mar 25Pushed 1w ago6 watchersCompare

[ Source](https://github.com/johnpaulmedina/laravel-usps)[ Packagist](https://packagist.org/packages/johnpaulmedina/laravel-usps)[ Docs](https://github.com/johnpaulmedina/laravel-usps/)[ RSS](/packages/johnpaulmedina-laravel-usps/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (10)Dependencies (8)Versions (173)Used By (0)

Laravel-USPS
============

[](#laravel-usps)

Full Laravel package for the **USPS API v3** (OAuth2). Covers all 20 USPS API domains: addresses, tracking, labels, international labels, domestic prices, international prices, service standards, locations, carrier pickup, containers, payments, campaigns, package campaigns, adjustments, disputes, appointments, shipping options, and SCAN forms.

> **Note:** The legacy USPS Web Tools XML API was retired in January 2026. This package uses the new OAuth2-based REST/JSON API at `apis.usps.com`.

> **Reference:** Official USPS API examples and OpenAPI specs at [github.com/USPS/api-examples](https://github.com/USPS/api-examples).

Requirements
------------

[](#requirements)

- PHP 8.1+
- Laravel 10, 11, 12, or 13
- USPS API credentials from [developer.usps.com](https://developer.usps.com)

Installation
------------

[](#installation)

```
composer require johnpaulmedina/laravel-usps
```

The service provider and facade are auto-discovered.

Configuration
-------------

[](#configuration)

Publish the config file:

```
php artisan vendor:publish --tag=usps-config
```

This creates `config/usps.php`:

```
return [
    'client_id' => env('USPS_CLIENT_ID', ''),
    'client_secret' => env('USPS_CLIENT_SECRET', ''),
];
```

Add your credentials to `.env`:

```
USPS_CLIENT_ID=your-client-id
USPS_CLIENT_SECRET=your-client-secret

```

Artisan Commands
----------------

[](#artisan-commands)

```
# Validate an address
php artisan usps:validate "1600 Pennsylvania Ave NW" --state=DC --zip=20500

# Track packages
php artisan usps:track 9400111899223456789012

# ZIP code lookup (city/state)
php artisan usps:zip 33101

# Calculate shipping rates
php artisan usps:price 20500 33101 16 --mail-class=PRIORITY_MAIL

# Delivery estimates
php artisan usps:standards 20500 33101

# Find USPS locations
php artisan usps:locations 33101 --type=post-office --radius=5
```

All commands output formatted tables to the console.

Usage
-----

[](#usage)

All API domains are accessible via the `Usps` facade. Address methods are called directly; all other domains are accessed through fluent accessor methods that return typed API clients.

### Addresses

[](#addresses)

```
use Usps;

// Validate an address
$result = Usps::validate([
    'Address' => '1600 Pennsylvania Ave NW',
    'City' => 'Washington',
    'State' => 'DC',
    'Zip' => '20500',
]);
```

**Response:**

```
{
    "address": {
        "Address2": "1600 PENNSYLVANIA AVE NW",
        "Address1": "",
        "City": "WASHINGTON",
        "State": "DC",
        "Zip5": "20500",
        "Zip4": "0005"
    },
    "additionalInfo": {
        "deliveryPoint": "00",
        "carrierRoute": "C000",
        "DPVConfirmation": "Y",
        "DPVCMRA": "N",
        "business": "Y",
        "centralDeliveryPoint": "",
        "vacant": "N"
    }
}
```

> **DPV (Delivery Point Validation)** confirms whether a specific address is a real USPS deliverable location — down to the apartment/suite level.
>
> CodeMeaning`Y`Full match — both street and unit confirmed`D`Primary address confirmed, apartment/suite **missing**`S`Primary confirmed, apartment/suite provided but **doesn't match**`N`Not a valid delivery point

**When apartment is missing (e.g., multi-unit building):**

```
{
    "address": {
        "Address2": "100 S BISCAYNE BLVD",
        "Address1": "",
        "City": "MIAMI",
        "State": "FL",
        "Zip5": "33131",
        "Zip4": "2011"
    },
    "corrections": [
        {
            "code": "32",
            "text": "Default address: The address you entered was found but more information is needed (such as an apartment, suite, or box number) to match to a specific address."
        }
    ],
    "additionalInfo": {
        "deliveryPoint": "99",
        "carrierRoute": "C038",
        "DPVConfirmation": "D",
        "DPVCMRA": "N",
        "business": "N",
        "centralDeliveryPoint": "",
        "vacant": "N"
    }
}
```

> **Correction codes:** `32` = apartment/suite needed, `22` = multiple addresses found.

```
// Full address lookup (raw v3 response)
$result = Usps::addressLookup([
    'streetAddress' => '1600 Pennsylvania Ave NW',
    'city' => 'Washington',
    'state' => 'DC',
]);
```

**Response:**

```
{
    "firm": "",
    "address": {
        "streetAddress": "1600 PENNSYLVANIA AVE NW",
        "streetAddressAbbreviation": "1600 PENNSYLVANIA AVE NW",
        "secondaryAddress": "",
        "cityAbbreviation": "WASHINGTON",
        "city": "WASHINGTON",
        "state": "DC",
        "ZIPCode": "20500",
        "ZIPPlus4": "0005",
        "urbanization": ""
    },
    "additionalInfo": { ... },
    "corrections": [],
    "matches": [{ "code": "31", "text": "Single Response - exact match" }]
}
```

```
// City/State lookup by ZIP
$result = Usps::cityStateLookup('20500');
```

**Response:**

```
{
    "city": "WASHINGTON",
    "state": "DC",
    "ZIPCode": "20500"
}
```

```
// ZIP Code lookup by address
$result = Usps::zipCodeLookup([
    'streetAddress' => '1600 Pennsylvania Ave NW',
    'city' => 'Washington',
    'state' => 'DC',
]);
```

**Response:**

```
{
    "firm": "",
    "address": {
        "streetAddress": "1600 PENNSYLVANIA AVE NW",
        "city": "WASHINGTON",
        "state": "DC",
        "ZIPCode": "20500",
        "ZIPPlus4": "0005"
    }
}
```

> **Note:** State names are auto-converted to abbreviations (`Florida` → `FL`). ZIP codes with dashes are auto-split (`20500-0005` → ZIP5: `20500`, ZIP4: `0005`).

> **USPS Address API Documentation:** [developer.usps.com/api/81](https://developer.usps.com/api/81) — full response field definitions, DPV codes, correction codes, and error handling.

### Tracking

[](#tracking)

```
// Track packages (supports up to 35 at once)
$result = Usps::tracking()->track([
    ['trackingNumber' => '9400111899223456789012'],
]);

// Register for email notifications
Usps::tracking()->registerNotifications('9400111899223456789012', [
    'uniqueTrackingID' => 'abc-123',
    'notifyEventTypes' => ['ALL_UPDATES'],
    'recipients' => [['email' => 'user@example.com']],
]);

// Request proof of delivery
Usps::tracking()->proofOfDelivery('9400111899223456789012', [
    'uniqueTrackingID' => 'abc-123',
    'recipients' => [['email' => 'user@example.com', 'firstName' => 'John', 'lastName' => 'Doe']],
]);
```

### Domestic Labels

[](#domestic-labels)

```
// Create a shipping label (requires payment authorization token)
$label = Usps::labels()->createLabel($labelData, $paymentToken);
$label = Usps::labels()->createLabel($labelData, $paymentToken, 'idempotency-key');

// Create a return label
$label = Usps::labels()->createReturnLabel($labelData, $paymentToken);

// Cancel/refund a label
Usps::labels()->cancelLabel('9205500000000000000000');

// Edit label attributes
Usps::labels()->editLabel('9205500000000000000000', $patchData);

// First-Class indicia
Usps::labels()->createIndicia($indiciaData, $paymentToken);

// Intelligent Mail Barcode
Usps::labels()->createImb($imbData, $paymentToken);
Usps::labels()->cancelImb('00000000000000000000');

// Label branding
Usps::labels()->uploadBranding($svgData);
Usps::labels()->listBranding(limit: 10, offset: 0);
Usps::labels()->getBranding('image-uuid');
Usps::labels()->deleteBranding('image-uuid');
Usps::labels()->renameBranding('image-uuid', ['name' => 'new-name']);

// Reprint a label
Usps::labels()->reprintLabel('9205500000000000000000', $reprintData, $paymentToken);
```

### International Labels

[](#international-labels)

```
$label = Usps::internationalLabels()->createLabel($labelData, $paymentToken);
Usps::internationalLabels()->reprintLabel('CX123456789US', $reprintData, $paymentToken);
Usps::internationalLabels()->cancelLabel('CX123456789US');
Usps::internationalLabels()->createIndicia($indiciaData, $paymentToken);
```

### Domestic Prices

[](#domestic-prices)

```
// Base rate search
$rate = Usps::domesticPrices()->baseRateSearch([
    'originZIPCode' => '20500',
    'destinationZIPCode' => '33101',
    'weight' => 2.5,
    'mailClass' => 'PRIORITY_MAIL',
]);

// Extra service rate search
$rate = Usps::domesticPrices()->extraServiceRateSearch($rateIngredients);

// List eligible products
$list = Usps::domesticPrices()->baseRateListSearch($rateIngredients);

// Total rates (base + extra services)
$total = Usps::domesticPrices()->totalRateSearch($rateIngredients);

// First-Class letter rates
$letter = Usps::domesticPrices()->letterRateSearch($rateIngredients);
```

### International Prices

[](#international-prices)

```
$rate = Usps::internationalPrices()->baseRateSearch($rateIngredients);
$rate = Usps::internationalPrices()->extraServiceRateSearch($rateIngredients);
$list = Usps::internationalPrices()->baseRateListSearch($rateIngredients);
$total = Usps::internationalPrices()->totalRateSearch($rateIngredients);
$letter = Usps::internationalPrices()->letterRateSearch($rateIngredients);
```

### Service Standards

[](#service-standards)

```
// Delivery estimates (with dates and acceptance locations)
$estimates = Usps::serviceStandards()->getEstimates('20500', '33101', [
    'mailClass' => 'PRIORITY_MAIL',
]);

// Service standards (average delivery days)
$standards = Usps::serviceStandards()->getStandards('20500', '33101');
```

### Service Standards Directory

[](#service-standards-directory)

```
// Get valid 5-digit ZIP codes
$zips = Usps::serviceStandardsDirectory()->getValidZip5Codes();

// Get directory report (paginated)
$report = Usps::serviceStandardsDirectory()->getReport([
    'originZIPCode' => '230',
    'destinationZIPCode' => '330',
    'responseFormat' => '3D_BASE',
    'mailClass' => 'PERIODICALS',
    'limit' => 10000,
]);
```

### Service Standards Files

[](#service-standards-files)

```
// List available files
$files = Usps::serviceStandardsFiles()->listFiles();

// Get a signed download URL
$download = Usps::serviceStandardsFiles()->generateSignedUrl('Combined_Service_Standard_Directory_MKT');
```

### International Service Standards

[](#international-service-standards)

```
$standard = Usps::internationalServiceStandards()->getServiceStandard('CA', 'PRIORITY_MAIL_INTERNATIONAL');
// Returns: { countryCode, mailClass, serviceStandardMessage }
```

### Locations

[](#locations)

```
// Dropoff locations for destination entry parcels
$locations = Usps::locations()->getDropoffLocations('33101', [
    'mailClass' => 'PARCEL_SELECT',
]);

// Post office locations
$offices = Usps::locations()->getPostOfficeLocations([
    'ZIPCode' => '33101',
    'radius' => 10,
]);

// Parcel locker locations
$lockers = Usps::locations()->getParcelLockerLocations(['ZIPCode' => '33101']);
```

### Carrier Pickup

[](#carrier-pickup)

```
// Check pickup eligibility
$eligible = Usps::carrierPickup()->checkEligibility('123 Main St', [
    'ZIPCode' => '33101',
    'state' => 'FL',
]);

// Schedule a pickup
$pickup = Usps::carrierPickup()->schedulePickup([
    'pickupDate' => '2026-04-01',
    'pickupAddress' => [...],
    'packages' => [['packageType' => 'PRIORITY_MAIL', 'packageCount' => 2]],
    'estimatedWeight' => 5.0,
    'pickupLocation' => ['packageLocation' => 'FRONT_DOOR'],
]);

// Get, update, cancel
$details = Usps::carrierPickup()->getPickup('WTC12345');
Usps::carrierPickup()->updatePickup('WTC12345', $data, $etag);
Usps::carrierPickup()->cancelPickup('WTC12345', $etag);
```

### Containers

[](#containers)

```
// Create a container label
$container = Usps::containers()->createContainer($containerData);

// Add/remove packages
Usps::containers()->addPackages('CNT-001', ['trackingNumbers' => [...]]);
Usps::containers()->removePackage('CNT-001', '9400111899223456789012');
Usps::containers()->removeAllPackages('CNT-001');

// Close container and generate manifest
Usps::containers()->createManifest(['containers' => ['CNT-001']]);
```

### Payments

[](#payments)

```
// Create payment authorization token (required for labels)
$auth = Usps::payments()->createPaymentAuthorization([
    'roles' => [
        ['roleName' => 'PAYER', 'CRID' => '12345678', 'accountType' => 'EPS', 'accountNumber' => '1234'],
        ['roleName' => 'LABEL_OWNER', 'CRID' => '12345678', 'MID' => '123456', 'manifestMID' => '123456'],
    ],
]);
$token = $auth['paymentAuthorizationToken'];

// Check account balance
$account = Usps::payments()->getPaymentAccount('12345678', 'EPS', ['amount' => 50.00]);
```

### Informed Delivery Campaigns (Mail)

[](#informed-delivery-campaigns-mail)

```
$campaign = Usps::campaigns()->createCampaign($campaignData);
$list = Usps::campaigns()->searchCampaigns(['status' => 'ACTIVE']);
$detail = Usps::campaigns()->getCampaign('CAM-001');
Usps::campaigns()->updateCampaign('CAM-001', $data);
Usps::campaigns()->cancelCampaign('CAM-001');
Usps::campaigns()->addImbs('CAM-001', ['imbs' => [...]]);

// Callback key management
$keys = Usps::campaigns()->getCallbackKeys('12345');
$summary = Usps::campaigns()->getCallbackSummary('12345', 'key-abc');
$details = Usps::campaigns()->getCallbackDetails('12345', 'key-abc');
```

### Informed Delivery Package Campaigns

[](#informed-delivery-package-campaigns)

```
$campaign = Usps::packageCampaigns()->createCampaign($campaignData);
$list = Usps::packageCampaigns()->searchCampaigns();
$detail = Usps::packageCampaigns()->getCampaign('PKG-001');
Usps::packageCampaigns()->updateCampaign('PKG-001', $data);
Usps::packageCampaigns()->cancelCampaign('PKG-001');
Usps::packageCampaigns()->addTrackingNumbers('PKG-001', ['trackingNumbers' => [...]]);
```

### Adjustments

[](#adjustments)

```
$adjustments = Usps::adjustments()->getAdjustments('12345678', '920011234561234567890', 'CENSUS');
$adjustments = Usps::adjustments()->getAdjustments('12345678', '920011234561234567890', 'DUPLICATES', [
    'destinationZIPCode' => '33101',
]);
```

### Disputes

[](#disputes)

```
$dispute = Usps::disputes()->createDispute([
    'EPSTransactionID' => 'TXN-001',
    'trackingID' => '920011234561234567890',
    'CRID' => '12345678',
    'reason' => 'INCORRECT_ASSESSED_WEIGHT',
    'description' => 'Weight was measured incorrectly.',
    'name' => 'John Doe',
    'disputeCount' => '1',
]);
```

### Appointments (FAST)

[](#appointments-fast)

```
// Check availability
$slots = Usps::appointments()->getAvailability(['facilityId' => 'FAC-001']);

// Create, update, cancel
$appt = Usps::appointments()->createAppointment($appointmentData);
Usps::appointments()->updateAppointment($updateData);
Usps::appointments()->cancelAppointment(['appointmentId' => 'APT-001']);
```

### Shipping Options

[](#shipping-options)

```
// Combined pricing + service standards + available options in one call
$options = Usps::shippingOptions()->search([
    'originZIPCode' => '20500',
    'destinationZIPCode' => '33101',
    'weight' => 2.5,
    'length' => 12,
    'width' => 8,
    'height' => 4,
    'mailClass' => 'PRIORITY_MAIL',
]);
```

### SCAN Forms

[](#scan-forms)

```
// Label shipment — link tracking numbers to a single EFN
$form = Usps::scanForms()->createLabelShipment([
    'trackingNumbers' => ['9400111899223456789012', '9400111899223456789013'],
]);

// MID shipment
$form = Usps::scanForms()->createMidShipment([
    'MID' => '123456',
    'trackingNumbers' => ['9400111899223456789012'],
]);

// Manifest MID shipment
$form = Usps::scanForms()->createManifestMidShipment([
    'manifestMID' => '654321',
    'MID' => '123456',
]);
```

API Domains
-----------

[](#api-domains)

DomainFacade AccessorEndpointsAddressesDirect methods3Tracking`tracking()`3Domestic Labels`labels()`12International Labels`internationalLabels()`4Domestic Prices`domesticPrices()`5International Prices`internationalPrices()`5Service Standards`serviceStandards()`2Service Standards Directory`serviceStandardsDirectory()`2Service Standards Files`serviceStandardsFiles()`2International Service Standards`internationalServiceStandards()`1Locations`locations()`3Carrier Pickup`carrierPickup()`5Containers`containers()`5Payments`payments()`2Campaigns`campaigns()`9Package Campaigns`packageCampaigns()`6Adjustments`adjustments()`1Disputes`disputes()`1Appointments`appointments()`4Shipping Options`shippingOptions()`1SCAN Forms`scanForms()`3Authentication
--------------

[](#authentication)

The package automatically handles OAuth2 client credentials flow. Access tokens are cached per-scope for ~50 minutes to minimize token requests. Each API domain requests only the scopes it needs.

License
-------

[](#license)

MIT

###  Health Score

55

—

FairBetter than 97% of packages

Maintenance87

Actively maintained with recent releases

Popularity40

Moderate usage in the ecosystem

Community20

Small or concentrated contributor base

Maturity62

Established project with proven stability

 Bus Factor1

Top contributor holds 77.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~100 days

Recently: every ~0 days

Total

30

Last Release

147d ago

Major Versions

7.0.2 → 8.0.x-dev2022-05-14

v8.0 → v9.02022-05-14

5.4.x-dev → v10.02023-06-30

v10.0 → v11.0.x-dev2025-01-13

v11.0 → v13.02026-03-25

PHP version history (9 changes)5.2.x-devPHP &gt;=5.4.0

v1.0.0PHP &gt;=7.1.0

6.0PHP ^7.2

7.0PHP ^7.2.5

8.0.x-devPHP ^7.3|^8.0

v9.0PHP ^8.0.2

5.4.x-devPHP &gt;=5.6.4

v10.0PHP ^8.1

v11.0.x-devPHP ^8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/93db769fc9a578d0b75ab6db17c2f260785b428475478ead1708524911935ee1?d=identicon)[johnpaulmedina](/maintainers/johnpaulmedina)

---

Top Contributors

[![johnpaulmedina](https://avatars.githubusercontent.com/u/1139439?v=4)](https://github.com/johnpaulmedina "johnpaulmedina (42 commits)")[![pdbreen](https://avatars.githubusercontent.com/u/1670907?v=4)](https://github.com/pdbreen "pdbreen (7 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (3 commits)")[![bredmor](https://avatars.githubusercontent.com/u/8249823?v=4)](https://github.com/bredmor "bredmor (1 commits)")[![neodisco](https://avatars.githubusercontent.com/u/15376698?v=4)](https://github.com/neodisco "neodisco (1 commits)")

---

Tags

address-validationlaravellaravel-packagelaravel13uspsusps-apiusps-migrationusps-v3laravelvalidationaddressoauth2uspsapi v3

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/johnpaulmedina-laravel-usps/health.svg)

```
[![Health](https://phpackages.com/badges/johnpaulmedina-laravel-usps/health.svg)](https://phpackages.com/packages/johnpaulmedina-laravel-usps)
```

###  Alternatives

[mike-bronner/laravel-model-caching

Automatic caching for Eloquent models.

2.4k161.4k2](/packages/mike-bronner-laravel-model-caching)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M354](/packages/psalm-plugin-laravel)[sandermuller/laravel-fluent-validation

Fluent validation rule builders for Laravel

21428.9k6](/packages/sandermuller-laravel-fluent-validation)[api-platform/laravel

API Platform support for Laravel

58190.1k21](/packages/api-platform-laravel)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

265.2k](/packages/aedart-athenaeum)[forjedio/inertia-table

Backend-driven dynamic tables for Laravel + Inertia.js

272.0k](/packages/forjedio-inertia-table)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
