Configuration
The package works with zero extra configuration once the key is set — every value has a default and most are env-backed. The published config/google-places.php:
return [
'key' => env('GOOGLE_PLACES_API_KEY'),
'hosts' => [
'places' => env('GOOGLE_PLACES_HOST', 'https://places.googleapis.com/v1'),
'routes' => env('GOOGLE_ROUTES_HOST', 'https://routes.googleapis.com'),
'geocoding' => env('GOOGLE_GEOCODING_HOST', 'https://maps.googleapis.com/maps/api'),
],
'field_masks' => [
'details' => 'id,displayName,formattedAddress,location,viewport,types,regularOpeningHours,photos',
'autocomplete' => 'suggestions.placePrediction.placeId,suggestions.placePrediction.text,suggestions.placePrediction.structuredFormat,suggestions.placePrediction.types',
'search' => 'places.id,places.displayName,places.formattedAddress,places.location,places.viewport,places.types,places.regularOpeningHours,places.photos',
'routes' => 'originIndex,destinationIndex,distanceMeters,duration,condition,status',
],
'http' => [
'timeout' => env('GOOGLE_PLACES_TIMEOUT', 10),
'connect_timeout' => env('GOOGLE_PLACES_CONNECT_TIMEOUT', 5),
'retries' => env('GOOGLE_PLACES_RETRIES', 2),
'retry_delay' => env('GOOGLE_PLACES_RETRY_DELAY', 200),
],
'cache' => [
'enabled' => env('GOOGLE_PLACES_CACHE', false),
'store' => env('GOOGLE_PLACES_CACHE_STORE'),
'ttl' => env('GOOGLE_PLACES_CACHE_TTL', 86400),
],
'pagination' => [
'max_pages' => env('GOOGLE_PLACES_MAX_PAGES', 5),
],
'logging' => [
'enabled' => env('GOOGLE_PLACES_LOGGING', false),
'channel' => env('GOOGLE_PLACES_LOG_CHANNEL'),
],
'rate_limits' => [
'owner' => env('GOOGLE_PLACES_RATELIMIT_OWNER', 'app'),
'places' => [
'enabled' => env('GOOGLE_PLACES_PLACES_RATELIMIT_ENABLED', true),
'limit' => env('GOOGLE_PLACES_PLACES_RATELIMIT', 600),
'per' => env('GOOGLE_PLACES_PLACES_RATELIMIT_PER', 'minute'),
'adaptive' => env('GOOGLE_PLACES_PLACES_RATELIMIT_ADAPTIVE', true),
'max_wait' => env('GOOGLE_PLACES_PLACES_RATELIMIT_MAX_WAIT'),
'jitter' => env('GOOGLE_PLACES_PLACES_RATELIMIT_JITTER'),
],
// 'routes' => [...] and 'geocoding' => [...] — same keys, own env prefix
],
];Every key
Every value is read strictly: a key that is not set takes its default, and anything invalid throws RoundlyConsulting\PackageToolkit\Exceptions\InvalidConfigurationException naming the key — never a silent fallback. Blank means not set: an absent key, null and a blank value (a host’s KEY=, empty or whitespace only) all take the default.
- A bool switch accepts true/false, 1/0, on/off or yes/no, from .env or the published file.
- An int takes an integer or an integer string ('30'). 'five', '5.5', '5s' or a value out of range throws. Timeouts, cache.ttl, pagination.max_pages and the rate-limit limit must be at least 1; retries, retry_delay, max_wait and jitter at least 0.
- rate_limits.{surface}.per must be exactly second, minute, hour or day.
- A string — hosts.*, field_masks.*, cache.store, logging.channel, rate_limits.owner — must be a string. A blank host is Google’s own, and a blank store, channel or owner the default. A field mask has no default: a blank one throws “required but missing”.
| Key | Default | Env | Purpose |
|---|---|---|---|
key | null | GOOGLE_PLACES_API_KEY | API key. Sent as X-Goog-Api-Key (Places/Routes) and key= (Geocoding). Required. |
hosts.places | https://places.googleapis.com/v1 | GOOGLE_PLACES_HOST | Places API (New) host. A string; blank = not set → Google’s own host. |
hosts.routes | https://routes.googleapis.com | GOOGLE_ROUTES_HOST | Routes API host (distance/ETA). A string; blank = not set → Google’s own host. |
hosts.geocoding | https://maps.googleapis.com/maps/api | GOOGLE_GEOCODING_HOST | Geocoding API host (reverse and forward geocoding). A string; blank = not set → Google’s own host. |
field_masks.details | see config | — | X-Goog-FieldMask for place details. Every mask must be a string; masks have no fallback, so a blank one throws (required but missing). |
field_masks.autocomplete | see config | — | X-Goog-FieldMask for autocomplete. |
field_masks.search | see config | — | X-Goog-FieldMask for text/nearby search. |
field_masks.routes | see config | — | X-Goog-FieldMask for distance and the route matrix. |
http.timeout | 10 | GOOGLE_PLACES_TIMEOUT | Request timeout (seconds), at least 1. |
http.connect_timeout | 5 | GOOGLE_PLACES_CONNECT_TIMEOUT | Connection timeout (seconds), at least 1. |
http.retries | 2 | GOOGLE_PLACES_RETRIES | Retries on connection failure, 0 or more. |
http.retry_delay | 200 | GOOGLE_PLACES_RETRY_DELAY | Delay between retries (ms), 0 or more. |
cache.enabled | false | GOOGLE_PLACES_CACHE | Cache idempotent lookups (details, geocoding, distance, matrix, search). |
cache.store | null | GOOGLE_PLACES_CACHE_STORE | Cache store; unset, null or blank = the default store. A non-string value throws. |
cache.ttl | 86400 | GOOGLE_PLACES_CACHE_TTL | Cache TTL (seconds), at least 1. |
pagination.max_pages | 5 | GOOGLE_PLACES_MAX_PAGES | Safety cap for the paginated search helpers, at least 1; a warning is logged when hit. |
logging.enabled | false | GOOGLE_PLACES_LOGGING | Log every request (endpoint, status, duration). The API key is never logged. |
logging.channel | null | GOOGLE_PLACES_LOG_CHANNEL | Log channel; unset, null or blank = the default channel. A non-string value throws. |
rate_limits.owner | app | GOOGLE_PLACES_RATELIMIT_OWNER | Bucket owner shared across surfaces (google-places:{surface}:{owner}). A string; blank = not set → app. |
rate_limits.{surface}.enabled | true | GOOGLE_PLACES_{SURFACE}_RATELIMIT_ENABLED | Throttle this surface (places / routes / geocoding); false = unthrottled. |
rate_limits.{surface}.limit | 600 | GOOGLE_PLACES_{SURFACE}_RATELIMIT | Max requests per window, at least 1. |
rate_limits.{surface}.per | minute | GOOGLE_PLACES_{SURFACE}_RATELIMIT_PER | Window: exactly second, minute, hour or day; anything else throws. |
rate_limits.{surface}.adaptive | true | GOOGLE_PLACES_{SURFACE}_RATELIMIT_ADAPTIVE | Self-tune from a 429 Retry-After. |
rate_limits.{surface}.max_wait | null | GOOGLE_PLACES_{SURFACE}_RATELIMIT_MAX_WAIT | Fail fast (ms, 0 or more) instead of pacing; null or blank = pace. |
rate_limits.{surface}.jitter | null | GOOGLE_PLACES_{SURFACE}_RATELIMIT_JITTER | Random spread (ms, 0 or more) added to a deferred call; null or blank = none. |
Field masks
The Places API (New) and the Routes API require an X-Goog-FieldMask header naming the fields to return — a request without one errors. The defaults are conservative; trim them so you pay only for the fields you use, or extend them when you need more. For example, typed address components on a place need addressComponents in the details mask:
// config/google-places.php — add addressComponents so Place::components() is populated
'field_masks' => [
'details' => 'id,displayName,formattedAddress,location,viewport,types,regularOpeningHours,photos,addressComponents',
// ...
],A single details request can also override its mask without touching config — see Place details.
Hosts
Each Google product lives on its own host. Override the hosts only when you proxy Google through your own gateway. Reverse and forward geocoding intentionally use the Geocoding API, which authenticates with the key query parameter; Places and Routes use the X-Goog-Api-Key header.
Environment
The common knobs are env-driven, so you rarely need to publish the config at all:
GOOGLE_PLACES_API_KEY=your-google-maps-api-key
GOOGLE_PLACES_TIMEOUT=10
GOOGLE_PLACES_CONNECT_TIMEOUT=5
GOOGLE_PLACES_RETRIES=2
GOOGLE_PLACES_RETRY_DELAY=200
GOOGLE_PLACES_CACHE=true
GOOGLE_PLACES_CACHE_TTL=86400
GOOGLE_PLACES_LOGGING=true
GOOGLE_PLACES_LOG_CHANNEL=stackShow 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 cryptoBy 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.