Google Maps API 403 Forbidden Error: Complete Fix Guide (+ Timeout Troubleshooting)
Fix Google Maps API 403 errors fast: enable billing, check API key restrictions, and enable the correct API in Google Cloud Console. Timeout fixes included.
- A 403 error from the Google Maps API almost always means billing is not enabled, the specific Maps API product is not activated in your Google Cloud project, or your API key's HTTP referrer / IP restriction is blocking the request
- API key restriction mismatches (e.g., a browser key used in a server-side request) silently return 403 even when billing and API enablement are correct
- Google Maps API timeouts are a separate issue caused by quota exhaustion, over-large payloads, or client-side network problems — they require a different diagnosis path from 403s
- Quick fix summary: (1) enable billing on the project, (2) enable the exact Maps API product (Maps JavaScript API, Geocoding API, etc.), (3) verify key restrictions match your runtime environment, (4) regenerate the key if in doubt
| Method | When to Use | Time to Apply | Risk / Downside |
|---|---|---|---|
| Enable billing in Google Cloud Console | Project has no active billing account — most common root cause | < 5 min | Requires credit card; small cost risk if quota limits not set |
| Enable the specific Maps API product | 403 body says 'API not enabled' or 'SERVICE_DISABLED' | < 2 min | None — purely additive change |
| Remove or loosen API key HTTP-referrer restriction | Key has referrer restriction that doesn't match your domain or localhost | < 2 min | Slightly broader key exposure until restriction is re-tightened |
| Rotate / regenerate the API key | Key is compromised, corrupted, or was created before billing was linked | 5–10 min | Must update key in all deployment environments |
| Set per-project quota limits and retry with exponential back-off | Rate-limit 429 or intermittent 403 due to quota burst | 30–60 min (code change) | Requires code changes and redeployment |
| Switch to a server-side key with IP restriction for backend calls | Browser key is being used in Node.js / server environment | 10–15 min | Need to manage two keys and separate deployment configs |
Understanding the Google Maps API 403 Error
HTTP 403 (Forbidden) from Google Maps APIs means Google's servers received your request but refused to fulfil it. Unlike a 401, a 403 is not about authentication — your API key was recognised, but the request was not authorised. The JSON error body is the single most important diagnostic artefact:
{
"error": {
"code": 403,
"message": "The provided API key is invalid.",
"status": "PERMISSION_DENIED"
}
}
Or, in the Maps JavaScript API, a red banner in the browser console:
Google Maps JavaScript API error: ApiNotActivatedMapError
https://developers.google.com/maps/documentation/javascript/error-messages#api-not-activated-map-error
Each distinct status string maps to a different root cause.
Step 1: Read the Exact Error Status Field
Open your browser DevTools Network tab (or check server logs) and find the failed Maps API request. Expand the response body and note the status value:
| status value | Root cause |
|---|---|
PERMISSION_DENIED |
Billing not enabled, API not enabled, or key restriction mismatch |
REQUEST_DENIED |
API key is invalid, expired, or was never given access to this API |
API_NOT_ACTIVATED |
The specific Maps product is not turned on in Cloud Console |
OVER_DAILY_LIMIT |
Billing issue or daily quota exceeded |
OVER_QUERY_LIMIT |
Per-second or per-day rate limit hit (leads to 429 but sometimes surfaces as 403) |
Step 2: Verify Billing Is Enabled
This is the single most common cause, especially for new projects or projects migrated after June 2018 (when Google made billing mandatory).
- Open console.cloud.google.com and select your project from the top dropdown.
- In the left sidebar go to Billing.
- Confirm that a billing account is linked. If you see "This project has no billing account", click Link a billing account and complete the setup.
- After linking, wait 2–5 minutes and retry your API call. Billing propagation is not instantaneous.
Free tier note: Google provides a $200 monthly Maps credit. You will not be charged immediately unless your usage exceeds this credit, but a valid payment method must still be on file.
Step 3: Enable the Specific Maps API Product
Enabling billing does not automatically enable every Maps API. Each product (Maps JavaScript API, Geocoding API, Places API, Directions API, Distance Matrix API, etc.) must be individually activated.
- In Cloud Console, navigate to APIs & Services → Library.
- Search for the exact API your application calls. For a web map embed it is Maps JavaScript API; for server-side geocoding it is Geocoding API.
- Click the product card, then click Enable.
- Repeat for every product your application uses.
You can also use the gcloud CLI:
gcloud services enable maps-backend.googleapis.com --project YOUR_PROJECT_ID
gcloud services enable geocoding-backend.googleapis.com --project YOUR_PROJECT_ID
gcloud services enable places-backend.googleapis.com --project YOUR_PROJECT_ID
List all currently enabled services to audit your project:
gcloud services list --enabled --project YOUR_PROJECT_ID | grep maps
Step 4: Check and Fix API Key Restrictions
Google offers three restriction types that can cause 403s:
A) HTTP Referrer Restrictions (browser / frontend keys)
If you set a referrer restriction like https://myapp.com/* and you test from http://localhost:3000, the key is rejected with PERMISSION_DENIED. Fix options:
- Add
http://localhost/*andhttp://localhost:3000/*to the allowed referrers list during development. - Use an unrestricted key locally and a restricted key in production (via environment variables).
B) IP Address Restrictions (server-side keys)
If you restrict the key to a specific server IP but your requests originate from a load balancer, a CI/CD runner, or a new server IP, the request will be denied. Verify your outbound IP:
curl -s https://api.ipify.org
Compare this against the allowed IPs in APIs & Services → Credentials → [Your Key] → Application restrictions.
C) API Restrictions (which APIs the key can call)
You may have explicitly restricted a key to only certain APIs. Ensure the Maps API products your code calls are listed under API restrictions → Restrict key → selected APIs.
Step 5: Troubleshoot Google Maps API Timeouts
Timeouts are unrelated to 403s but frequently co-occur when rate limits are being hit.
Symptom: Requests hang for 5–30 seconds then fail with a network timeout, or the Maps JavaScript API fires the error event with no response body.
Causes and fixes:
Quota exhaustion (soft timeout): When your per-second QPS limit is hit, Google queues requests until they time out client-side. Check APIs & Services → Quotas for red bars. Request a quota increase or implement exponential back-off.
Large Directions / Distance Matrix requests: The API enforces payload limits (e.g., 25 waypoints per Directions request). Batch your requests into smaller chunks.
Client network issues: Use
curlto directly test the API endpoint from your server to rule out DNS or firewall issues:
curl -v "https://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Parkway&key=YOUR_API_KEY"
- Implement retry logic with back-off: Any production Maps integration should handle transient failures:
async function geocodeWithRetry(address, apiKey, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const delay = Math.pow(2, attempt) * 200; // 200ms, 400ms, 800ms
if (attempt > 0) await new Promise(r => setTimeout(r, delay));
try {
const res = await fetch(
`https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(address)}&key=${apiKey}`
);
if (res.status === 403) throw new Error(`403 PERMISSION_DENIED`);
if (!res.ok) continue;
return await res.json();
} catch (e) {
if (attempt === maxRetries - 1) throw e;
}
}
}
Step 6: Regenerate the API Key (Last Resort)
If all above steps pass and you still receive 403, the key itself may be in a bad state (created before billing was attached, partially corrupted in Google's backend, or leaked and subsequently invalidated by Google's key scanning).
- APIs & Services → Credentials → Create Credentials → API Key
- Apply the same restrictions as the old key.
- Update your environment variables / secrets manager.
- Delete the old key after confirming the new one works.
Never hard-code API keys in source code. Use environment variables or a secrets manager such as Google Secret Manager, AWS Secrets Manager, or HashiCorp Vault.
Frequently Asked Questions
#!/usr/bin/env bash
# Google Maps API 403 / Timeout Diagnostic Script
# Usage: MAPS_API_KEY=YOUR_KEY ./diagnose_maps.sh
set -euo pipefail
API_KEY="${MAPS_API_KEY:-}"
if [[ -z "$API_KEY" ]]; then
echo "ERROR: Set MAPS_API_KEY environment variable before running."
exit 1
fi
PROJECT_ID="${GCLOUD_PROJECT:-}"
echo "=== 1. Basic Geocoding API health check ==="
RESPONSE=$(curl -s -w "\n%{http_code}" \
"https://maps.googleapis.com/maps/api/geocode/json?address=New+York&key=${API_KEY}")
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | head -n -1)
echo "HTTP Status: $HTTP_CODE"
echo "API Status: $(echo "$BODY" | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d.get(\"status\",\"unknown\"))' 2>/dev/null || echo 'parse error')"
if [[ "$HTTP_CODE" == "403" ]]; then
echo "ERROR MESSAGE: $(echo "$BODY" | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d.get(\"error_message\",\"see body\"))' 2>/dev/null)"
fi
echo ""
echo "=== 2. Places API health check ==="
PLACES_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \
"https://maps.googleapis.com/maps/api/place/textsearch/json?query=coffee&key=${API_KEY}")
echo "HTTP Status: $PLACES_RESPONSE"
echo ""
echo "=== 3. Directions API health check ==="
DIR_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \
"https://maps.googleapis.com/maps/api/directions/json?origin=NYC&destination=Boston&key=${API_KEY}")
echo "HTTP Status: $DIR_RESPONSE"
echo ""
echo "=== 4. Outbound IP (for IP restriction debugging) ==="
curl -s https://api.ipify.org && echo ""
echo ""
echo "=== 5. DNS and connectivity to Google APIs ==="
curl -s -o /dev/null -w "DNS: %{time_namelookup}s | Connect: %{time_connect}s | TTFB: %{time_starttransfer}s | Total: %{time_total}s\n" \
"https://maps.googleapis.com/maps/api/geocode/json?address=test&key=${API_KEY}"
if [[ -n "$PROJECT_ID" ]]; then
echo ""
echo "=== 6. Enabled Maps services (requires gcloud auth) ==="
gcloud services list --enabled --project "$PROJECT_ID" --filter="name:maps OR name:geocoding OR name:places OR name:directions" 2>/dev/null || echo "gcloud not authenticated — skipping"
else
echo ""
echo "=== 6. Skipped: set GCLOUD_PROJECT to check enabled services ==="
fi
echo ""
echo "=== Diagnosis complete ==="Error Medic Editorial
Error Medic Editorial is a team of senior DevOps, SRE, and backend engineers who specialise in cloud API integrations, infrastructure troubleshooting, and developer experience. Our guides are validated against live environments and updated whenever platform behaviour changes.
Sources
- https://developers.google.com/maps/documentation/javascript/error-messages
- https://developers.google.com/maps/faq#over-limit-key-error
- https://cloud.google.com/apis/docs/getting-started#enabling_apis
- https://stackoverflow.com/questions/14817769/google-maps-api-key-error-denied
- https://issuetracker.google.com/issues/35823198
- https://developers.google.com/maps/documentation/javascript/usage-and-billing