Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Zipcode Laravel Package

pragmarx/zipcode

pragmarx/zipcode is a Laravel package for validating and formatting postal codes. It provides country-aware zip code rules and helpers, making it easy to add reliable address and postal-code handling to forms, APIs, and applications.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require pragmarx/zipcode

Laravel 5.5+ will auto-discover PragmaRX\ZipCode\Vendor\Laravel\ServiceProvider and register the ZipCode facade alias. On older versions add both manually to config/app.php:

PragmaRX\ZipCode\Vendor\Laravel\ServiceProvider::class,
'ZipCode' => PragmaRX\ZipCode\Vendor\Laravel\Facade::class,

Publish the config so you can see which providers are wired up:

php artisan vendor:publish --provider="PragmaRX\ZipCode\Vendor\Laravel\ServiceProvider" --tag=config

Open config/zipcode.php first — that's where you'll discover the provider list (Google Maps, Bing, OpenStreetMap/Nominatim, Here, plus a bundled Brazilian CEP database). Most providers require an API key in .env; the Brazilian one is the only one that works fully offline because the CEP data ships inside the package.

The first successful use case is usually a form submission that resolves a CEP into a full address:

use PragmaRX\ZipCode\Vendor\Laravel\Facade as ZipCode;

$result = ZipCode::find('01001-000');

if ($result && $result->found()) {
    return [
        'street'  => $result->getStreet(),
        'city'    => $result->getCity(),
        'state'   => $result->getState(),
        'country' => $result->getCountry(),
        'lat'     => $result->getLatitude(),
        'lng'     => $result->getLongitude(),
    ];
}

find() returns an object regardless of success, so always check found() (or notFound()). toArray() gives you a flat dump if you're persisting the result.

Implementation Patterns

Wrap lookups in a service class, not controllers. The facade is a thin layer over Geocoder; you want one place to normalize results and one place to swap providers later.

class AddressResolver
{
    public function lookup(string $zip): ?array
    {
        return Cache::remember("zipcode:{$zip}", now()->addDays(30), function () use ($zip) {
            $r = ZipCode::find($zip);
            return $r->found() ? $r->toArray() : null;
        });
    }
}

Cache aggressively. Every provider call is an outbound HTTP request. Zip codes are effectively immutable, so a 30-day TTL is fine. Cache the failure case too (with a shorter TTL) so a typo'd zip doesn't hammer the API on every keystroke.

Normalize the input before calling find(). Strip dashes and whitespace (01001-000 → 01001000) — some providers accept the formatted version and some don't, and you don't want that to be the difference between a hit and a miss.

Build a provider fallback chain. In config/zipcode.php you can register multiple providers; the practical pattern is to try the Brazilian database first (free, fast, offline) and fall back to Google for non-Brazilian addresses. Wrap that in your resolver class rather than relying on a single global default, because a single default means every user pays for Google Calls.

Use it during async jobs for bulk imports. If you're back-filling thousands of addresses, dispatch queued jobs (one zip each) that call the resolver, so you get rate-limiting and retries for free. Do not loop over ZipCode::find() inside a request — you'll blow the provider's quota and time out.

Validate before lookup with Laravel's existing rules. preg_match('/^\d{5}-?\d{3}$/', $cep) for BR, or use PostalCode-style validation per-country. Garbage inputs waste API calls and often come back as ambiguous geocodes rather than clean errors.

Gotchas and Tips

The package hasn't been released since October 2019. Pinned Geocoder constraints may conflict with newer Laravel/PHP versions. Before committing, verify it installs cleanly against your PHP version; if not, fork it or move to geocoder-php/* directly — the package is a fairly thin wrapper around it.

find() returning an object is not success. It's easy to write $result->getCity() and get null while the object silently represents a failure. Always guard on found().

Google requires a billing-enabled API key, not just a free key — without billing enabled the request fails and you'll see an empty result, not an exception. Check $result->getErrorMessage() when debugging; the failure reason is usually there.

Nominatim (OpenStreetMap) requires a User-Agent and is rate-limited to ~1 req/sec. If the config doesn't let you set one, you'll get blocked. This is a common trap when picking a "free" provider.

Brazilian CEP data is frozen at package-release time. New neighborhoods from the last several years won't be in the bundled JSON. For production BR coverage, plan to refresh the database or layer an external CEP service on top.

No built-in caching or retry logic. Both are your responsibility. Cache at the resolver layer and use Http::retry()-ish behavior around the call (or Laravel's queue retries).

Result objects are per-provider. Google and Nominatim won't populate the same fields — e.g. "neighborhood" is unreliable outside BR providers. Treat getNeighborhood() as best-effort and don't rely on it in UI without a fallback.

Locale/region hints matter. Ambiguous zips (e.g. 1000 matches many countries) get resolved against whatever default region the provider guesses. If your app is BR-only, hard-code the Brazilian provider rather than relying on geocoding heuristics.

Extension point: the provider list in config/zipcode.php is the only real extension surface. Swapping a provider means editing that file and clearing config cache (php artisan config:clear) — a stale cached config is the most common "my new API key isn't being picked up" bug.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource