Google Maps API 403 Error: Fix REQUEST_DENIED, ApiNotActivatedMapError & Timeouts
Fix Google Maps API 403 errors fast: enable billing, check API key restrictions, whitelist referrers, and resolve timeout issues with step-by-step commands.
- A 403 from the Maps API almost always means billing is not enabled on your Google Cloud project, the specific Maps API product is not activated, or your API key's HTTP referrer / IP restrictions are blocking the request
- Timeout errors are typically caused by rate-limiting (QPS exceeded), overly restrictive client-side fetch timeouts, or a key that silently falls back to a degraded unauthenticated tier
- The fastest diagnostic is to hit the REST endpoint directly with curl using your key and inspect the JSON error body — the 'status' field (REQUEST_DENIED, OVER_QUERY_LIMIT, etc.) tells you exactly which fix path to take
| Method | When to Use | Time to Fix | Risk |
|---|---|---|---|
| Enable billing on GCP project | New project, Maps API never worked | 2 minutes | Low — free tier still applies |
| Activate the specific Maps API product | 403 with status ApiNotActivatedMapError | 1 minute | None |
| Whitelist HTTP referrer in key restrictions | 403 only in browser, works in curl | 3 minutes | Low — test in staging first |
| Add server IP to allowed IPs list | 403 only from backend / server-side calls | 2 minutes | Low |
| Rotate API key (new unrestricted key) | Fastest way to confirm a restriction is the cause | 5 minutes | Medium — update all consumers |
| Increase client timeout / add retry with backoff | Intermittent timeouts, not consistent 403 | 30 minutes | Low — pure client-side change |
| Request quota increase in Cloud Console | OVER_QUERY_LIMIT on sustained high traffic | 1–3 business days | None |
Understanding Google Maps API 403 Errors
A 403 Forbidden from a Google Maps API endpoint means the server understood your request but refuses to fulfil it due to an authorization problem — not a missing resource (404) and not a server crash (5xx). Google wraps this in a JSON envelope:
{
"error": {
"code": 403,
"message": "The provided API key is invalid.",
"status": "PERMISSION_DENIED"
}
}
For the JavaScript Maps SDK loaded in a browser you will instead see a console error such as:
Google Maps JavaScript API error: ApiNotActivatedMapError
https://developers.google.com/maps/documentation/javascript/error-messages#api-not-activated-map-error
or:
Google Maps JavaScript API warning: NoApiKeys
Google Maps JavaScript API error: RefererNotAllowedMapError
The status field in the REST response (or the error code suffix in the JS API) is your primary diagnostic signal.
Step 1: Reproduce and Capture the Raw Error
Before changing anything, get the exact error body so you are fixing the right problem.
For REST / Places / Geocoding / Directions APIs:
curl -s "https://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Pkwy&key=YOUR_API_KEY" | python3 -m json.tool
Expected 403 response bodies and what they mean:
status value |
Root cause |
|---|---|
REQUEST_DENIED |
Billing disabled, API not enabled, or key restrictions blocking the caller |
PERMISSION_DENIED |
OAuth scope missing (service account use-case) or key completely invalid |
OVER_QUERY_LIMIT |
Per-second or per-day quota exhausted — presents as 429 in newer APIs but 403 in legacy Maps APIs |
For the JavaScript Maps API, open DevTools → Network tab, filter by maps.googleapis.com, and look at the response body of the js?key=... request.
Step 2: Verify Billing Is Enabled
This is the single most common cause for projects created after June 2018.
- Go to console.cloud.google.com/billing.
- Ensure a billing account is linked to the project that owns the key.
- A linked billing account does not mean you will be charged immediately — Google provides a $200/month free tier for Maps.
You can also check via gcloud:
gcloud beta billing projects describe YOUR_PROJECT_ID --format='value(billingEnabled)'
# Expected: True
Step 3: Confirm the Correct API Product Is Enabled
Each Maps product (Geocoding, Directions, Places, Maps JavaScript API, Static Maps, etc.) must be individually enabled in the API Library.
# List enabled APIs for your project
gcloud services list --project=YOUR_PROJECT_ID --enabled | grep -i maps
# Enable the Maps JavaScript API
gcloud services enable maps-backend.googleapis.com --project=YOUR_PROJECT_ID
# Enable the Geocoding API
gcloud services enable geocoding-backend.googleapis.com --project=YOUR_PROJECT_ID
# Enable the Places API
gcloud services enable places-backend.googleapis.com --project=YOUR_PROJECT_ID
Common API service names:
| Product | Service Name |
|---|---|
| Maps JavaScript API | maps-backend.googleapis.com |
| Geocoding API | geocoding-backend.googleapis.com |
| Directions API | directions-backend.googleapis.com |
| Places API | places-backend.googleapis.com |
| Distance Matrix API | distance-matrix-backend.googleapis.com |
Step 4: Audit API Key Restrictions
Key restrictions are the second most common cause of a 403 that "works in one place but not another."
# View current restrictions on your key (requires project owner role)
gcloud alpha services api-keys describe YOUR_KEY_ID --project=YOUR_PROJECT_ID
HTTP Referrer restrictions (browser-side key):
If your key has referrer restrictions set to https://yourdomain.com/* but you are testing from localhost:3000, the API will return 403. Add localhost as an allowed referrer during development:
Allowed referrers:
http://localhost:3000/*
https://yourdomain.com/*
https://www.yourdomain.com/*
Note: referrer restrictions do not work for server-side calls because Node.js / Python / curl do not send a Referer header. Server-side calls must use an IP address restriction or an unrestricted key stored in a secret manager.
IP address restrictions (server-side key):
# Find your outbound IP
curl -s https://api.ipify.org
# Add that IP (or CIDR range) to the key's IP restrictions in Cloud Console
Step 5: Diagnose Timeout Errors
Timeout errors (ETIMEDOUT, net::ERR_TIMED_OUT, or a 408/504 returned by a proxy) have different root causes:
5a. QPS (Queries Per Second) rate limiting:
The Maps Geocoding API has a default limit of 50 QPS. If you spike above that, subsequent requests queue and eventually time out on the client side even though the server would eventually respond with OVER_QUERY_LIMIT.
# Check current quota usage
gcloud monitoring metrics list --filter='metric.type=serviceruntime.googleapis.com/api/request_count' 2>/dev/null
# Or inspect in Cloud Console: APIs & Services → Maps API → Quotas
Add exponential backoff in your code:
import time, requests
from random import uniform
def geocode_with_backoff(address, api_key, max_retries=5):
url = "https://maps.googleapis.com/maps/api/geocode/json"
for attempt in range(max_retries):
resp = requests.get(url, params={"address": address, "key": api_key}, timeout=10)
data = resp.json()
if data["status"] == "OK":
return data
if data["status"] == "OVER_QUERY_LIMIT":
wait = (2 ** attempt) + uniform(0, 1)
time.sleep(wait)
continue
raise RuntimeError(f"Maps API error: {data['status']} — {data.get('error_message', '')}")
raise RuntimeError("Max retries exceeded")
5b. Client-side fetch timeout too short:
The Maps JavaScript API loader can take 1–3 seconds on a cold load. If your wrapper sets a 1-second timeout, it will fail intermittently on slower connections.
// Too aggressive:
const mapPromise = Promise.race([
loadGoogleMaps(),
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 1000))
]);
// Better — 10 seconds for initial load:
const mapPromise = Promise.race([
loadGoogleMaps(),
new Promise((_, reject) => setTimeout(() => reject(new Error('Maps load timeout')), 10000))
]);
5c. Verify the endpoint is reachable from your network:
curl -v --max-time 10 "https://maps.googleapis.com/maps/api/geocode/json?address=test&key=YOUR_KEY"
# Look for: * Connected to maps.googleapis.com
# If it hangs: check firewall rules, DNS resolution, or proxy configuration
# DNS check:
nslookup maps.googleapis.com
dig maps.googleapis.com +short
Step 6: Rotate and Harden Your Key
If you cannot determine which restriction is causing the 403, the fastest way to isolate the issue is to create a temporary unrestricted key:
gcloud alpha services api-keys create --display-name='debug-maps-key' --project=YOUR_PROJECT_ID
# Note the key string from the output, test with it
# If it works: the problem was a restriction on your original key
# Delete the debug key immediately after testing:
gcloud alpha services api-keys delete DEBUG_KEY_ID --project=YOUR_PROJECT_ID
Once confirmed working, add restrictions back incrementally — first HTTP referrer or IP, then API target restrictions — retesting after each change.
Step 7: Check Cloud Console Error Logs
# Stream live API errors from Cloud Logging
gcloud logging read \
'protoPayload.serviceName="maps-backend.googleapis.com" severity>=ERROR' \
--project=YOUR_PROJECT_ID \
--limit=50 \
--format='table(timestamp, protoPayload.status.code, protoPayload.status.message)'
This will surface server-side errors that your client code may be swallowing.
Frequently Asked Questions
#!/usr/bin/env bash
# google-maps-api-debug.sh
# Usage: API_KEY=your_key PROJECT_ID=your_project bash google-maps-api-debug.sh
set -euo pipefail
KEY="${API_KEY:?Set API_KEY env var}"
PROJECT="${PROJECT_ID:-}"
echo "=== 1. Test basic geocode request ==="
curl -s "https://maps.googleapis.com/maps/api/geocode/json?address=1600+Amphitheatre+Pkwy&key=${KEY}" \
| python3 -m json.tool 2>/dev/null || echo "[WARN] python3 not available, raw output:"
echo ""
echo "=== 2. Test Places API (Nearby Search) ==="
curl -s "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=-33.8670522,151.1957362&radius=500&key=${KEY}" \
| python3 -c "import sys,json; d=json.load(sys.stdin); print('STATUS:', d.get('status')); print('ERROR:', d.get('error_message','none'))"
echo ""
echo "=== 3. DNS resolution ==="
nslookup maps.googleapis.com | grep -E '(Name|Address)'
echo ""
echo "=== 4. TLS connectivity check ==="
curl -sv --max-time 10 "https://maps.googleapis.com/" 2>&1 | grep -E '(Connected|SSL|HTTP/|< HTTP)' | head -10
echo ""
echo "=== 5. Outbound IP (for IP restriction whitelist) ==="
curl -s https://api.ipify.org && echo ""
if [[ -n "${PROJECT}" ]]; then
echo ""
echo "=== 6. Check which Maps APIs are enabled ==="
gcloud services list --project="${PROJECT}" --enabled --filter='name:*maps* OR name:*geocoding* OR name:*places* OR name:*directions*' \
--format='table(name, title)' 2>/dev/null || echo "[SKIP] gcloud not available or insufficient permissions"
echo ""
echo "=== 7. Check billing status ==="
gcloud beta billing projects describe "${PROJECT}" --format='value(billingEnabled)' 2>/dev/null \
&& echo "(True = billing enabled)" || echo "[SKIP] gcloud billing not available"
fi
echo ""
echo "=== Done. Check STATUS fields above for root cause. ==="
echo "REQUEST_DENIED -> billing disabled or API not enabled or key restriction mismatch"
echo "OVER_QUERY_LIMIT -> quota exceeded, add retry/backoff"
echo "INVALID_REQUEST -> malformed parameters"
echo "OK -> key and API are working correctly"Error Medic Editorial
The Error Medic Editorial team is composed of senior DevOps engineers, cloud architects, and SRE practitioners with collective experience managing production API integrations at scale. We specialize in translating cryptic cloud provider errors into actionable, evidence-based fix guides.
Sources
- https://developers.google.com/maps/documentation/javascript/error-messages
- https://developers.google.com/maps/faq#keysystem
- https://developers.google.com/maps/documentation/geocoding/requests-geocoding#StatusCodes
- https://cloud.google.com/apis/docs/errors
- https://stackoverflow.com/questions/14521450/google-maps-v3-api-key-request-denied