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

Every outbound call is paced through http-client-rate-limits-for-laravel, on a per-surface budget keyed google-places:{surface}:{owner}. Requests wait for their window to free up by default; with adaptive on (the default), a 429 Retry-After from Google self-tunes the limiter. Each surface defaults to 600 requests per minute.

SurfaceCallsHost key
placesautocomplete, details, textSearch, nearbySearch, findPlace, textSearchPaginated, nearbySearchPaginated, photoUri, photoContents (photo())hosts.places
routesdistance, computeMatrix, matrix()hosts.routes
geocodinggeocode, geocodeAddresshosts.geocoding

Tune each surface in config/google-places.php (see Configuration) or via env:

GOOGLE_PLACES_GEOCODING_RATELIMIT=300
GOOGLE_PLACES_GEOCODING_RATELIMIT_PER=minute
GOOGLE_PLACES_ROUTES_RATELIMIT_ENABLED=false   # send this surface unthrottled
GOOGLE_PLACES_PLACES_RATELIMIT_MAX_WAIT=2000    # fail fast (ms) instead of pacing

rate_limits.owner is shared across surfaces, so several apps sharing one Google key can each carry their own budget. photoUrl() (no request) and check() are never throttled. Every key is read by its full name, so a per typo or a junk limit throws InvalidConfigurationException naming, say, google-places.rate_limits.places.limit — it never quietly paces per minute.

Failing fast

When a surface is set to fail fast (max_wait) and the wait would exceed it, the call throws RateLimitExceededException — a PlacesException, so existing catch (PlacesException) sites keep working — carrying $e->surface and, through the package toolkit’s HasRetryAfter contract, $e->retryAfterSeconds():

use RoundlyConsulting\PackageToolkit\Contracts\HasRetryAfter;

try {
    GooglePlaces::textSearch('coffee');
} catch (PlacesException $e) {
    if ($e instanceof HasRetryAfter) {
        return response('Slow down', 429, ['Retry-After' => $e->retryAfterSeconds()]);
    }

    throw $e;
}
use RoundlyConsulting\GooglePlaces\Exceptions\RateLimitExceededException;

catch (RateLimitExceededException $e) {
    $e->surface;              // "places", "routes" or "geocoding"
    $e->retryAfterSeconds();  // seconds until the window frees up
    $e->getCode();            // 429
}

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.