API Key Management and Security

1. Introduction

Historically, Google API keys were used to access Google APIs when other methods were unavailable or considered inconvenient. The popular use cases were access to the Google Maps API and Google APIs exposed by Firebase. With the introduction of AI models, AI agents like Gemini, and AI Studio and Agent development frameworks like Agent Development Kit, API keys have become a primary method to access Google's Large Language Models.

API keys offer a low level of protection. Although Google Cloud provides several methods to prevent misuse of the keys, possessing an active API key allows access to Google APIs without any additional authentication or authorization validations. The methods of restricting the use of API keys are described in documentation. Securing Your Gemini and Google API keys post in Cloud Blog gives additional recommendation about API key maintenance. In this Codelab, you will put these recommendations into practice.

What you'll do

  • Review the enforced restrictions when creating new API keys on Google Cloud
  • Catalog all your API keys, and find the keys with no security protection
  • Enforce restrictions to the existing API keys based on its usage
  • Define automation that deletes the key in event of the abnormal use

What you'll need

  • A modern web browser (such as Chrome).
  • A Google Account

2. Setup

The instructions in this Codelab assume that you run commands in Cloud Shell in the Google Cloud console. If you have gcloud CLI in your local environment, you can run the commands there.

While operations in the steps can be done using the Cloud console UI, the methods are different. This Codelab uses the command line interface to simplify interactions and enable easier integration with modern AI agents (like Antigravity CLI).

Start a Cloud Shell terminal

  1. Open the Google Cloud console using https://console.cloud.google.com/ in a new browser window. It is recommended to use Chrome for the best user experience.
  2. Sign in into your Google Account in Google Cloud.
  3. Click Activate Cloud Shell Activate Cloud Shell icon at the top of the Google Cloud console.
    If shown, click through the following windows:
    • Continue through the Cloud Shell information window.
    • Authorize Cloud Shell to use your credentials to make Google Cloud API calls.

Select a Google Cloud project

After you open the Cloud console, you are authenticated, and there is usually a project select for your work. The project ID is a a 6 to 30 character sequence of lowercase letters, numbers and dash symbols, for example qwiklabs-gcp-04-3075fc9fd77f. The Cloud Shell terminal configures gcloud CLI with the selected project. You will see an output similar to the following:

Your Cloud Platform project in this session is set to qwiklabs-gcp-04-3075fc9fd77f

It means that your further commands to gcloud will use the project ID qwiklabs-gcp-04-3075fc9fd77f.

Set the project ID as an environment variable PROJECT_ID. You can see the list of all your projects using the following command:

gcloud projects list
  • Replace your-project-id and run the command if you want to use a project ID that is different from the configured in gcloud.
    export PROJECT_ID="your-project-id"
    
    For example:
    export PROJECT_ID="qwiklabs-gcp-04-3075fc9fd77f"
    
  • Run the following command if you want to use the selected project ID:
    export PROJECT_ID=$(gcloud config get project)
    

3. Restrict a New API Key

In the past, users could create completely unrestricted API keys. Unrestricted keys could be used to call ANY Google API enabled in the project where the key was created. While the Google Cloud console prevents users from creating unrestricted keys, it is still possible using gcloud CLI or using direct API calls.

The following steps show how to create a restricted API key that limits the use to the specific API and by the specified website.

  1. To create a new API key restricted to use with Google Map geolocation API only run the following command in the shell terminal:
    gcloud services api-keys create --key-id=restricted-api-key \
      --display-name="restricted api key" \
      --api-target=service=geolocation.googleapis.com \
      --project=${PROJECT_ID}
    
    This command creates an new API key that can be used ONLY to call Google Map geolocation service.
  2. Increase the key security by adding an application restriction. Limit the use of the key to all paths within the website example.com only. Run the following command to add the application restriction to the key:
    gcloud services api-keys update restricted-api-key \
      --location=global \
      --allowed-referrers="example.com/*" \
      --project=${PROJECT_ID}
    
    Instead of allowing the use of the key to specific website(s), you can use --allowed-application to define allowed Android application orallowed-ips to define allowed IP addresses. Consult complete documentation for all options.

Clean up

Delete the API key you created unless you plan to use it:

gcloud services api-keys delete --key-id=restricted-api-key \
  --project=${PROJECT_ID}

4. Catalog Your API Keys

In this step you will use gcloud CLI to get an inventory of your API keys. The resulted list shows all active (not deleted) API keys that you have access to.

  1. Run the following command to see all key names, IDs and create dates:
    gcloud services api-keys list --project=${PROJECT_ID} \
      --format='value(displayName,name.basename(),createTime.date())'
    
    The output would show the human readable name of the key, the key ID and the date that the key was created. It would similar to the following:
    api key 1	api-key-1	2024-05-10T07:53:24
    api key 2	api-key-2	2025-06-12T14:47:57
    
  2. Pick one of the key ID's and paste the following command to check if the key has any restrictions. Substitute your-key-id with the value of the selected key ID:
    gcloud services api-keys describe "your-key-id" --project=${PROJECT_ID}
    

The output (in YAML) will contain a list of restrictions under restrictions.

createTime: '2024-05-10T07:53:24.986528Z'
displayName: api key 1
etag: W/"u1WuY41K2tPKUZd7cfLoKg=="
name: projects/123456789012/locations/global/keys/api-key-1
restrictions:
  apiTargets:
  - service: geolocation.googleapis.com
  browserKeyRestrictions:
    allowedReferrers:
    - https://example.com/*
uid: 1a2b3c4d-1234-abcd-1234-a1b2c3d4e5f6
updateTime: '2024-05-10T07:53:24.071228Z'

Note that if the key was never updated the createTime and updateTime fields will have the same timestamp.

  1. Download and run the script that walks through all your projects and prints out all API keys which have NO restrictions:
    curl -fsSL -o unrestricted_api_keys.sh \
      "https://github.com/GoogleCloudPlatform/devrel-demos/blob/main/security/api-key-audit/unrestricted_api_keys.sh"
    chmod +x unrestricted_api_keys.sh
    ./unrestricted_api_keys.sh
    
    After running the script you will see an output in the form of:
    DISPLAY NAME    KEY ID    PROJECT ID    CREATION DATE
    Key 1    1a2b3c4d-1234-abcd-1234-a1b2c3d4e5f6    my-project-1    2024-05-10T07:53:24.071228Z
    
    You can find all scripts used in this CodeLab at Security folder in the devrel-demos repository on GitHub.

5. Discover Usage of an API Key

In this step you will query Google Cloud metrics that help you to find what APIs were called using your API key. Using this information you can review the current use of the keys and apply API restrictions on the key based on actual information instead of making a guess.

  1. Use the same key ID that you used in the previous step or pick another key ID. Substitute your-key-id with the chosen key ID in the following command:
    export KEY_UID=$(
       gcloud services api-keys describe "your-key-id" \
       --format='value(uid)' \
       --project=${PROJECT_ID})
    
  2. Set the search to look back for one year usage history. If you want to look for longer or shorter period of time, replace the 365 (number of days) with another positive number.
    export DAYS=365
    
  3. Refresh Application Default Credentials (ADC) to enable direct call to Cloud Monitoring API. Run the following command and follow instructions in the terminal:
    gcloud auth application-default login
    
  4. Run the following command to send a request for service usage metric data to Cloud Monitoring API:
curl -s -G -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  --data-urlencode "filter=metric.type=\"serviceruntime.googleapis.com/api/request_count\" AND resource.labels.credential_id=\"apikey:${KEY_UID}\"" \
  --data-urlencode "interval.startTime=$(date -u -d "${DAYS} days ago" +%Y-%m-%dT%H:%M:%SZ)" \
  --data-urlencode "interval.endTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  "https://monitoring.googleapis.com/v3/projects/${PROJECT_ID}/timeSeries" \
  | jq -r '.timeSeries[]?.resource.labels.service' | sort -u

The command queries the serviceruntime/api/request_count built-in metric for data points with the label credential_id that match the chosen API key unique ID. Then it retrieves the values for the service label and prints the values while eliminating repetitions.

Hardening API key

In this step you will use information gathered in the previous steps to update the restriction configuration of the API key based on the usage info.

You will use the same API key that was used in the previous step. If necessary, re-run the instructions from the previous steps to ensure that the environment variables PROJECT_ID, KEY_UID and DAYS are set.

  1. Run the following command to retrieve the list of Google APIs called using the API key:

SERVICES=$(curl -s -G -H "Authorization: Bearer $(gcloud auth application-default print-access-token)"
–data-urlencode "filter=metric.type="serviceruntime.googleapis.com/api/request_count" AND resource.labels.credential_id="apikey:${KEY_UID}""
–data-urlencode "interval.startTime=$(date -u -d "${DAYS} days ago" +%Y-%m-%dT%H:%M:%SZ)"
–data-urlencode "interval.endTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
"https://monitoring.googleapis.com/v3/projects/${PROJECT_ID}/timeSeries"
| jq -r ‘.timeSeries[]?.resource.labels.service' | sort -u)

1. Build the list of arguments to restrict the API usage for the API key based
on the retrieved list.

```shell
API_TARGET_ARGS=()
for SERVICE in $SERVICES; do
  API_TARGET_ARGS+=("--api-target=service=${SERVICE}")
done
  1. Replace the list of restricted APIs for the non-empty list:
    if [ ${#API_TARGET_ARGS[@]} -gt 0 ]; then
        gcloud services api-keys update "projects/${PROJECT_ID}/locations/global/keys/${KEY_UID}" \
        ${API_TARGET_ARGS}
    fi
    

6. Define Anomaly Usage Detection

Previous steps showed how to exploring and hardening API keys. This step shows how to automate a response to unexpected peak of the key's usage with the help of Monitoring Alerts.

The following instructions create an alert that is fired when a rate of the API calls that use an API key increases by more than 10% over last 5 minutes. Alert is configured to trigger a Cloud Build script which deletes the API key to prevent further usage. The key can be restored during the next 30 days. See documentation to learn how to undelete the key.

The instructions re-use the variables PROJECT_ID and KEY_UID that you used in the previous steps. If you'd like to select a different key and/or project, set the new values for these variables as described in the Setup and Discover Usage of an API Key steps.

  1. Run the following script to create an alert policy file:
    cat <<EOF > alert_policy.json
    {
      "displayName": "Credential API Request Count Increase Alert (Project: ${PROJECT_ID})",
      "combiner": "OR",
      "conditions": [
        {
          "displayName": "API Request Count Increase > 10% in 5m with Min Volume",
          "conditionPrometheusQueryLanguage": {
            "query": "(sum(increase(serviceruntime_googleapis_com:api_request_count{metric_label_credential_id=\\"apikey:${KEY_UID}\\"}[5m])) / (sum(increase(serviceruntime_googleapis_com:api_request_count{metric_label_credential_id=\\"apikey:${KEY_UID}\\"}[5m] offset 5m)) or on() vector(1)) > 1.10) and (sum(increase(serviceruntime_googleapis_com:api_request_count{metric_label_credential_id=\\"apikey:${KEY_UID}\\"}[5m])) > 50)",
            "duration": "0s",
            "evaluationInterval": "60s"
          }
        }
      ],
      "enabled": true
    }
    EOF
    
    The alert policy uses the following PromQL filter to trigger the alert:
     (sum(
       increase(
         serviceruntime_googleapis_com:api_request_count{metric_label_credential_id="API_KEY_UID"}[5m])
     ) /
     (sum(
       increase(
         serviceruntime_googleapis_com:api_request_count{metric_label_credential_id="API_KEY_UID"}[5m] offset 5m)
     ) or on() vector(1)) > 1.10)
    and
     (sum(
       increase(
         serviceruntime_googleapis_com:api_request_count{metric_label_credential_id=\"YOUR_CREDENTIAL_ID_HERE\"}[5m])) > 50)
    
    It calculates the rate of increase and compares it to a previous window. And triggers the alert only if it is more than 10% greater. To avoid triggering the alert when the total number of calls is neglectable, it conditions the triggering to have more than 50 API calls in the window. To avoid NaN (delete by zero) calculation when the previous 5 minute rate was 0, it substitutes denominator with 1 if the previous window rate is zero.You can change the alert parameters such as the window length (5m), the minimal threshold (50) or the 10% increase threshold (1.10).Additional policy parameters define that the once the condition is reached, the alert should be fired (duration) and that the condition should be probed each 60 seconds (evaluationInterval).
  2. Run the following command to create a PubSub topic that will be used to post alert notifications:
    gcloud pubsub topics create api-key-alert-notifications --project=$PROJECT_ID
    
  3. Run the following command to create a notification channels for alerts that uses PubSub.
    CHANNEL_NAME=$(gcloud beta monitoring channels create \
      --display-name="Pub/Sub Alert Channel" \
      --type="pubsub" \
      --channel-labels="topic=projects/$PROJECT_ID/topics/api-key-alert-notifications" \
      --format='value(name)' \
      --project=$PROJECT_ID)
    
    You will use the CHANNEL_NAME environment variable in the Clean Up step.
  4. Run the following command to create a new monitoring alert:
    gcloud monitoring policies create --policy-from-file=alert_policy.json \
      --project=$PROJECT_ID
    
  5. Run the following command to grant Cloud Build service permissions to delete the API keys in the project.
    PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
    gcloud projects add-iam-policy-binding $PROJECT_ID \
      --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
      --role="roles/apikeys.admin"
    
    It is possible to limit the apikeys.admin role to manipulate only specific instance of the API keys. See IAM conditions for more details.
  6. Run the following script to create a Cloud Build trigger that deletes the API key.
    cat <<EOF > trigger_config.yaml
    name: "delete-compromised-api-key"
    description: "Triggered by Pub/Sub alert to automatically delete the leaking API Key"
    pubsubConfig:
      topic: "projects/${PROJECT_ID}/topics/api-key-alert-notifications"
    build:
      steps:
      - name: "gcr.io/google.com/cloudsdktool/cloud-sdk:slim"
        args:
        - "gcloud"
        - "services"
        - "api-keys"
        - "delete"
        - "${KEY_UID}"
        - "--quiet"
    EOF
    
  7. Run the following command to create a new Monitoring Alert trigger:
    gcloud builds triggers create pubsub \
      --trigger-config=trigger_config.yaml \
      --project=$PROJECT_ID
    

You can now delete the alert policy and Cloud Build trigger config files:

rm alert_policy.json trigger_config.yaml

Alternatively, you can set up this automation using Terraform plan. Download the Terraform files from the abnormal-usage-detection folder in the Google Cloud DevRel repository. The plan accepts a project ID and an API Key UID as input parameters and set up the resources and configurations you saw in this step.

7. Clean up

To avoid incurring unexpected charges on your Google Cloud account, remember to delete the Pub/Sub topic, Cloud Build trigger, and alert policies created during this exercise.

Run the following commands to delete all resources you created:

gcloud builds triggers delete delete-compromised-api-key \
  --project=$PROJECT_ID
gcloud beta monitoring channels delete $CHANNEL_NAME \
  --project=$PROJECT_ID \
  --quiet
gcloud pubsub topics delete api-key-alert-notifications \
  --project=$PROJECT_ID
gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
  --role="roles/apikeys.admin"

8. Summary

In this codelab, you implemented a robust end-to-end security and automation framework for Google Cloud API keys:

  1. Hardened Default Configurations: You created and restricted API keys to limit access exclusively to necessary APIs and trusted platforms (such as specific HTTP referrers).
  2. Audited your Key Inventory: You scanned your project environments to detect and isolate unrestricted keys that present an immediate security risk.
  3. Analyzed Usage Data: You programmatically queried Cloud Monitoring metric data to profile historical key utilization, enabling you to restrict keys based on verified usage footprints.
  4. Automated Threat Mitigation: You established a reactive "circuit breaker" by wiring a Cloud Monitoring alert policy to a Pub/Sub topic and a Cloud Build trigger, allowing you to automatically delete compromised keys during abnormal traffic spikes.

Next Steps

  • Apply restrictions to all your API keys: Use what you learned in this lab to detect all partially restricted or unrestricted API keys and apply API and client restrictions,
  • Set up "circuit breaker" on API keys: Further protect your API keys from unexpected use by setting up automatic key deletion in event of the sudden consumption increase. Use gcloud commands or Terraform shown in the lab. Consider tighten permissions by utilizing IAM conditions
  • Explore Monitoring Alerting: Learn more about setting up alerts using Google Cloud Monitoring service.
  • Learn more about access control available at Google Cloud: Review the Access Boundary Policies and access change propagation.