Error Medic

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.

Last updated:
Last verified:
2,012 words
Key Takeaways
  • 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
Fix Approaches Compared
MethodWhen to UseTime to FixRisk
Enable billing on GCP projectNew project, Maps API never worked2 minutesLow — free tier still applies
Activate the specific Maps API product403 with status ApiNotActivatedMapError1 minuteNone
Whitelist HTTP referrer in key restrictions403 only in browser, works in curl3 minutesLow — test in staging first
Add server IP to allowed IPs list403 only from backend / server-side calls2 minutesLow
Rotate API key (new unrestricted key)Fastest way to confirm a restriction is the cause5 minutesMedium — update all consumers
Increase client timeout / add retry with backoffIntermittent timeouts, not consistent 40330 minutesLow — pure client-side change
Request quota increase in Cloud ConsoleOVER_QUERY_LIMIT on sustained high traffic1–3 business daysNone

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.

  1. Go to console.cloud.google.com/billing.
  2. Ensure a billing account is linked to the project that owns the key.
  3. 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

bash
#!/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"
E

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

Related Articles in Google Maps API

Explore More API Errors Guides