NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages

Published to config/geolocation.php. The full file, with the per-provider rate_limits blocks collapsed (they are covered in Rate limiting):

use RoundlyConsulting\Geolocation\Providers\DefaultLocationProvider;
use RoundlyConsulting\Geolocation\Providers\GoogleProvider;
use RoundlyConsulting\Geolocation\Providers\IP2LocationProvider;
use RoundlyConsulting\Geolocation\Providers\IpInfoProvider;
use RoundlyConsulting\Geolocation\Providers\MaxMindDatabaseProvider;
use RoundlyConsulting\Geolocation\Providers\MaxMindWebServiceProvider;

return [
    'pipeline' => ['maxmind_database', 'maxmind_web', 'ip2location', 'ipinfo', 'google', 'default'],

    'providers' => [
        'maxmind_database' => MaxMindDatabaseProvider::class,
        'maxmind_web' => MaxMindWebServiceProvider::class,
        'ip2location' => IP2LocationProvider::class,
        'ipinfo' => IpInfoProvider::class,
        'google' => GoogleProvider::class,
        'default' => DefaultLocationProvider::class,
    ],

    'timeout' => env('GEOLOCATION_TIMEOUT', 5),

    'cache' => [
        'enabled' => env('GEOLOCATION_CACHE', false),
        'store' => env('GEOLOCATION_CACHE_STORE'),
        'ttl' => env('GEOLOCATION_CACHE_TTL', 86400),
        'prefix' => env('GEOLOCATION_CACHE_PREFIX', 'geolocation'),
    ],

    'events' => [
        'enabled' => env('GEOLOCATION_EVENTS', true),
    ],

    'default' => [
        'humanReadable' => env('GEOLOCATION_DEFAULT_HUMAN_READABLE', ''),
        'street' => env('GEOLOCATION_DEFAULT_STREET', ''),
        'city' => env('GEOLOCATION_DEFAULT_CITY', ''),
        'country' => env('GEOLOCATION_DEFAULT_COUNTRY_ISO_CODE', ''),
        'latitude' => env('GEOLOCATION_DEFAULT_LATITUDE', 0.0),
        'longitude' => env('GEOLOCATION_DEFAULT_LONGITUDE', 0.0),
    ],

    'services' => [
        'ipinfo' => [
            'url' => env('IPINFO_URL', 'https://ipinfo.io/'),
            'token' => env('IPINFO_TOKEN'),
            'retry' => env('IPINFO_RETRY_TIMES', 3),
            'retry_delay' => env('IPINFO_RETRY_DELAY_MS', 100),
            'rate_limits' => [/* see Rate limiting */],
        ],

        'google' => [
            'url' => env('GOOGLE_MAPS_URL', 'https://maps.googleapis.com/maps/api'),
            'key' => env('GOOGLE_MAPS_API_KEY'),
            'retry' => env('GOOGLE_MAPS_RETRY_TIMES', 3),
            'retry_delay' => env('GOOGLE_MAPS_RETRY_DELAY_MS', 100),
            'rate_limits' => [/* ... */],
        ],

        'ip2location' => [
            'url' => env('IP2LOCATION_URL', 'https://api.ip2location.io'),
            'key' => env('IP2LOCATION_API_KEY'),
            'retry' => env('IP2LOCATION_RETRY_TIMES', 2),
            'retry_delay' => env('IP2LOCATION_RETRY_DELAY_MS', 100),
            'rate_limits' => [/* ... */],
        ],

        'maxmind_web' => [
            'enabled' => env('MAXMIND_WEB_ENABLED', false),
            'base_url' => env('MAXMIND_WEB_URL', 'https://geoip.maxmind.com/geoip/v2.1'),
            'account_id' => env('MAXMIND_ACCOUNT_ID'),
            'license_key' => env('MAXMIND_LICENSE_KEY'),
            'service' => env('MAXMIND_WEB_SERVICE', 'city'), // city|country|insights
            'retry' => env('MAXMIND_WEB_RETRY_TIMES', 2),
            'retry_delay' => env('MAXMIND_WEB_RETRY_DELAY_MS', 100),
            'rate_limits' => [/* ... */],
        ],

        'maxmind_database' => [
            'enabled' => env('MAXMIND_DB_ENABLED', false),
            'path' => env('MAXMIND_DB_PATH', storage_path('app/geolocation/GeoLite2-City.mmdb')),

            // Used by the geolocation:db:update command to download the .mmdb file.
            'license_key' => env('MAXMIND_LICENSE_KEY'),
            'edition' => env('MAXMIND_DB_EDITION', 'GeoLite2-City'),
            'download_url' => env('MAXMIND_DB_DOWNLOAD_URL', 'https://download.maxmind.com/app/geoip_download'),
        ],
    ],
];

Every key

KeyDefaultEnvPurpose
pipelineall six names—Ordered provider names to consult; the first non-empty result wins and an unreachable provider is skipped. A bundled name you removed from providers is skipped; any other unregistered name throws UnknownProviderException, and a non-list throws InvalidConfigurationException. An empty list consults providers in its own order.
providersbundled map—Name → provider class. Every entry needs a name; an unnamed one throws UnknownProviderException.
timeout5GEOLOCATION_TIMEOUTHTTP timeout in seconds for every HTTP-backed provider, at least 1. A per-call timeout override is held to the same rule (a blank one is not set, so the config applies).
cache.enabledfalseGEOLOCATION_CACHECache successful lookups and distances (never the default fallback).
cache.storenullGEOLOCATION_CACHE_STORECache store; null or blank = the default store. A non-string value throws.
cache.ttl86400GEOLOCATION_CACHE_TTLCache TTL in seconds, at least 1.
cache.prefixgeolocationGEOLOCATION_CACHE_PREFIXCache key prefix. A blank value is not set (the default); a non-string value throws.
events.enabledtrueGEOLOCATION_EVENTSDispatch resolution events.
default.humanReadable''GEOLOCATION_DEFAULT_HUMAN_READABLEFallback location’s display name.
default.street''GEOLOCATION_DEFAULT_STREETFallback street.
default.city''GEOLOCATION_DEFAULT_CITYFallback city.
default.country''GEOLOCATION_DEFAULT_COUNTRY_ISO_CODEFallback ISO country code.
default.latitude0.0GEOLOCATION_DEFAULT_LATITUDEFallback latitude, -90 to 90. A blank value is not set (0.0); a non-number throws.
default.longitude0.0GEOLOCATION_DEFAULT_LONGITUDEFallback longitude, -180 to 180. A blank value is not set (0.0); a non-number throws. While every default.* value is empty or zero (as shipped), the default provider answers null.
services.ipinfo.urlhttps://ipinfo.io/IPINFO_URLIPinfo base URL. A blank value is not set (the default); a non-string value throws.
services.ipinfo.tokennullIPINFO_TOKENIPinfo token; omitted when null (unauthenticated request).
services.ipinfo.retry3IPINFO_RETRY_TIMESRetry attempts, 0 or more.
services.ipinfo.retry_delay100IPINFO_RETRY_DELAY_MSRetry delay in ms, 0 or more.
services.google.urlhttps://maps.googleapis.com/maps/apiGOOGLE_MAPS_URLGoogle Maps API base URL. A blank value is not set (the default); a non-string value throws.
services.google.keynullGOOGLE_MAPS_API_KEYGoogle Maps API key.
services.google.retry3GOOGLE_MAPS_RETRY_TIMESRetry attempts, 0 or more.
services.google.retry_delay100GOOGLE_MAPS_RETRY_DELAY_MSRetry delay in ms, 0 or more.
services.ip2location.urlhttps://api.ip2location.ioIP2LOCATION_URLIP2Location.io base URL. A blank value is not set (the default); a non-string value throws.
services.ip2location.keynullIP2LOCATION_API_KEYIP2Location.io API key.
services.ip2location.retry2IP2LOCATION_RETRY_TIMESRetry attempts, 0 or more.
services.ip2location.retry_delay100IP2LOCATION_RETRY_DELAY_MSRetry delay in ms, 0 or more.
services.maxmind_web.enabledfalseMAXMIND_WEB_ENABLEDEnable the MaxMind web-service provider.
services.maxmind_web.base_urlhttps://geoip.maxmind.com/geoip/v2.1MAXMIND_WEB_URLWeb-service base URL. A blank value is not set (the default); a non-string value throws.
services.maxmind_web.account_idnullMAXMIND_ACCOUNT_IDMaxMind account ID (HTTP Basic user).
services.maxmind_web.license_keynullMAXMIND_LICENSE_KEYMaxMind license key (HTTP Basic password); withToken('maxmind_web', …) replaces it per call.
services.maxmind_web.servicecityMAXMIND_WEB_SERVICEcity, country or insights; a blank value is not set (city); anything else throws InvalidConfigurationException.
services.maxmind_web.retry2MAXMIND_WEB_RETRY_TIMESRetry attempts, 0 or more.
services.maxmind_web.retry_delay100MAXMIND_WEB_RETRY_DELAY_MSRetry delay in ms, 0 or more.
services.maxmind_database.enabledfalseMAXMIND_DB_ENABLEDEnable the local .mmdb provider.
services.maxmind_database.pathstorage_path('app/geolocation/GeoLite2-City.mmdb')MAXMIND_DB_PATHPath to the .mmdb file.
services.maxmind_database.license_keynullMAXMIND_LICENSE_KEYLicense key used by geolocation:db:update.
services.maxmind_database.editionGeoLite2-CityMAXMIND_DB_EDITIONEdition the update command downloads. A blank value is not set (the default); a non-string value throws.
services.maxmind_database.download_urlhttps://download.maxmind.com/app/geoip_downloadMAXMIND_DB_DOWNLOAD_URLDownload URL base. A blank value is not set (the default); a non-string value throws.
services.<provider>.rate_limits.*see Rate limitingGEOLOCATION_<PROVIDER>_RATELIMIT*Outbound pacing for google, ipinfo, ip2location and maxmind_web.

Every boolean switch (cache.enabled, events.enabled, the MaxMind enabled flags and the rate_limits enabled / adaptive keys) accepts true/false, 1/0, on/off or yes/no, from .env or the published file; an unset or blank one takes its default, and anything else (say GEOLOCATION_CACHE=disabled) throws package-toolkit’s InvalidConfigurationException naming the key on the first lookup.

Every other setting is just as strict. An integer must be a whole number (30 or "30"; five, 5.5 and 1e3 throw rather than becoming 0), a string setting must be a string, and a fixed vocabulary (per, services.maxmind_web.service) rejects anything outside it. A key that is not set — absent, null or blank (a host’s KEY=) — takes its default (an optional max_wait / jitter stays off).

Environment

Credentials are read from your application’s environment, so they never live in the package or in version control. A typical production .env:

GEOLOCATION_TIMEOUT=5
GEOLOCATION_CACHE=true
GEOLOCATION_CACHE_STORE=redis
GEOLOCATION_EVENTS=true

IPINFO_TOKEN=your-ipinfo-token
GOOGLE_MAPS_API_KEY=your-google-maps-key
IP2LOCATION_API_KEY=your-ip2location-key

MAXMIND_DB_ENABLED=true
MAXMIND_LICENSE_KEY=your-license-key

services.maxmind_web.license_key and services.maxmind_database.license_key both read MAXMIND_LICENSE_KEY, so one key covers the web service and database downloads.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.