Google Cloud bills most cloud expenses to projects. But which project?
The answer is easy for a Resource-based service like Google Compute Engine — the charge is assigned to the project that contains the Resource, such as a VM.
However, when you call an API for a Google Cloud service such as Vision, Translate, or BigQuery, the billing model is more flexible. Some or all charges are tied to a "Client" project that you specify, supporting cost attribution to different business units or projects.
This blog post will explain:
- What the "Client" project means and how to specify it.
- The main patterns for attaching billing to a project, across different types of services.
- Why Google documentation almost exclusively uses the phrase “quota project” when talking about billing, and how that relates to the “billing project.”
- How to discover, for any given API invocation, which project it was billed to.
- How to overcome the challenges of attributing costs to business categories.
Setting the Quota/Billing Project
When an API call reaches Google Cloud infrastructure, it evaluates the following hierarchy to determine the Client project, which for non-Resource services, is also used for billing. (I detail the concepts of “quota project” vs “billing project” below.)
- The evaluation is done by Google’s Service Infrastructure/Service Control, which provides a unified behavior across Google-managed services.
- Permissions: To allow specification of a project as Client, the invoking principal — such as a Service Account, user, or Workforce Identity Federation or Workload Identity Federation — must have the Service Usage Consumer role in that project. (It is not relevant whether a Service Account is defined in that project or another.)
- This applies to Google Cloud Platform services, but not necessarily other Google services like Workspace or Maps.
- You specify the Client project with the following techniques, in this precedence order.
Ways to Specify the Client
1. Specified in request
- HTTP Header: The
X-goog-user-projectHTTP header can be passed directly in the request. Useful in raw REST calls, for example with curl. - Client Library Configuration: The project that is set via client options in Google Cloud SDKs. Here is an example using the Python Google Cloud Client library.
from google.cloud import translate_v3from google.api_core.client_options import ClientOptions# 1. Define the billing projectoptions = ClientOptions(quota_project_id="your-billing-project-id")# 2. Inject it when creating the clientclient = translate_v3.TranslationServiceClient(client_options=options)2. Environment variable
The value of the environment variable GOOGLE_CLOUD_QUOTA_PROJECT. Defining this value is a best practice for dynamically setting the quota project in containerized environments without hardcoding it via ClientOptions.
3. API Key
The project where the API Key used to make the call was generated.
4. ADC Quota Project
For the local environment (usually in development), this is set with gcloud auth application-default set-quota-project.
The Local Development Trap: If you run
gcloud auth application-default loginwithout setting a Client project for quota and billing, your local scripts will authenticate most APIs with a special, limited project (the "Cloud SDK project"764086051850). Because this is shared globally, usage is severely throttled, and"Quota exceeded for project 764086051850"errors arise almost immediately. This can be avoided by setting the Client value.
5. Authentication Token
- Service Account: The project that owns the Service Account making the request (including impersonated Service Accounts).
- OAuth Client ID: The project that owns the OAuth 2.0 Client ID used to generate the end-user token.
6. Workforce Identity Federation
If the principal for the API is a Workforce Identity Federation user, the workforce pools user project is used.
Why does Google talk about “Quota project” and not about “Billing project”?
Google Cloud documentation and parameters mostly use the term “quota project”, while the term “billing project” is rarely used.
There are exceptions. For example, the gcloud documentation confusingly uses both terms side-by-side, even though the discussion is about the same parameter: billing/quota_project for the persistent tool configuration, and --billing-project for the command-line flag.
Here is the explanation. Across Google Cloud, the Client settings do one thing at the infrastructure level: They set the quota project used by the API gateway, while billing goes to the Resource project. However, for non-Resource-based services, the billing system finds no server-side Resource to bill, and so billing defaults to the quota project.
Resource-based vs. non-Resource-based services
The primary difference is between services billed by Resources and those without. Let's start with a summary:
| Service Type | Primary Billing Driver | Primary Quota Driver | Examples |
|---|---|---|---|
| Resource-based | Project containing the resource | Client Project | Compute Engine, Spanner, Cloud Run |
| Client-based | Client (Quota) Project | Client Project | Vision API, Translate API |
Resource-based APIs: Identifiable Server-Side Project.
These are Google’s infrastructure, storage, and compute APIs. They are stateful. When you call them, you are asking Google to read, modify, or delete a specific asset or data that lives in a project. The billing project is where the Resource sits, and is usually specified in the URL. In contrast, the quota project is determined by the Client setting.
There are some special cases like Cloud Storage, Pub/Sub and BigQuery, which are detailed below.
Some examples:
- Compute Engine API: Managing virtual machines, disks, and networks.
GET https://compute.googleapis.com/compute/v1/projects/{project}/zones/{zone}/instances - Cloud Spanner API: Querying or managing relational databases.
POST https://spanner.googleapis.com/v1/projects/{project}/instances/{instance}/databases/{database}/sessions - Cloud Run is “serverless,” but you are in charge of the Cloud Run service, not Google, and so has Resource-based billing. The URL for the Cloud Run service has no project and is not at the domain
googleapis.com.
Client-based quota and billing: No Server-Side Resource
For stateless APIs that are simply a globally shared algorithm, there is no Resource in any project and no server appears in the REST URL. For these, billing is determined by the Client setting.
Some examples:
- Cloud Natural Language API: You pass it a paragraph, and it returns sentiment analysis.
POST https://language.googleapis.com/v1/documents:analyzeSentiment - Cloud Video Intelligence API: You pass it a video, and it returns labels of objects inside it.
POST https://videointelligence.googleapis.com/v1/videos:annotate
Cloud Storage: The Requester Pays option
Cloud Storage is a Resource-based project, even though it doesn't have a project ID in the URL of this form: GET https://storage.googleapis.com/storage/v1/b/{bucket}/o/{object}
Still, it has a server-side project, which is looked up by the API gateway based on the bucket name.
As with other Resource-based APIs, billing is assigned to the bucket’s project, while the quota is assigned to the specified Client.
However, this can be changed with the "Requester Pays" option, which is unique to Cloud Storage. When the bucket owner enables Requester Pays, the invoker of the API must supply a billable project (via the Client settings mentioned above), and that project is billed for the egress and API operations (though the bucket owner still pays for storage).
BigQuery: Two separate Resource types
BigQuery is unusual in that storage and compute can live in different projects.
- Storage is billed to the project that owns the Resource: the dataset with the stored data.
- Query Processing is billed to the project where the query job runs. Even though a job is a type of compute, the job is treated as a distinct, stateful Resource. It is billed to its project, which can differ from the storage project and differ from where the job is initiated.
Pub/Sub: Topics and Subscriptions can be in different projects
Like other Resource-based services, financial costs in Pub/Sub are tied to Resource ownership, regardless of who makes the API call, and the Client settings are used only to calculate the quotas.
However, Pub/Sub is unusual in that the Resources themselves can be distributed across projects:
- The Topic is the relevant Resource that determines the billing for publishing messages.
- A Pub/Sub subscription is its own Resource, which can be created in a separate project from the Topic’s project or that of the invoking Client. This determines the billing for the delivery throughput, data transfer (egress), and subscription-level storage.
Finding the Billed Project for a Service Invocation
Here are some ways to find the project that is billed for a given invocation. There is no standard “billed_project” field inside a Google Cloud API log, but in general, the data can be discovered.
Prerequisite: To track API calls, you must first enable Data Access Audit logs for the specific API (e.g., Cloud Storage, BigQuery) in the project you suspect is being billed. Do this in the Google Cloud console at IAM & Admin -> Audit Logs.
For Google Cloud services in general
Filter the logs for the relevant service:
logName=~"cloudaudit.googleapis.com"protoPayload.serviceName="[SERVICE_NAME].googleapis.com"And optionally by method, for example
logName=~"cloudaudit.googleapis.com/data_access"protoPayload.serviceName="vision.googleapis.com"protoPayload.methodName="google.cloud.vision.v1.ImageAnnotator.AnnotateImage"Locate the resource.labels.project_id field within the log entry. This field identifies the billed project whether that is the Resource-based project or the Client-based project. (The exception is in Cloud Storage Requester Pays buckets.) This information is visible even when viewing a mix of project data through a multi-project Log Sink.
For BigQuery Compute Jobs
Because BigQuery separates compute from storage, you must look specifically at the compute execution logs rather than the storage logs. Run this query in Logs Explorer, and, as with other Google services, locate the resource.labels.project_id field.
resource.type="bigquery_project"protoPayload.methodName=("google.cloud.bigquery.v2.JobService.InsertJob" OR "google.cloud.bigquery.v2.JobService.Query")protoPayload.metadata."@type"="type.googleapis.com/google.cloud.audit.BigQueryAuditMetadata"For GCS Requester Pays
For Cloud Storage Requester Pays buckets, the audit logs show the billed project number (in hexadecimal) under protoPayload.authorizationInfo.resource, following “projects/”, for example as “projects/0000004d9813b13”.
The Solution for Cost Attribution
Once you know how to surface these billing logs, the next challenge is mapping them to your business: for example, to specific business departments or business projects.
My job as Cloud Architect at DoiT International is to advise customers on how to optimize costs, and DoiT offers a suite of powerful tools in DoiT Cloud Intelligence to make that happen. For example, DoiT Cost Allocation groups and attributes costs by business dimension.
Some services support labels that can help with cost attribution:
- For Resource-based services, you can allocate by Resource, optionally adding labels to the Resource to match your business categories.
- Exceptionally, the API invocation for many Generative AI models can receive custom cost labels to be used in attribution.
- Vertex AI pipelines also offer a unique cost-attribution feature. The Pipeline itself carries a small cost, but more importantly, it can spin off multiple types of resources. Vertex AI automatically attaches the
vertex-ai-pipelines-run-billing-idlabel to each Resource that it spins off, which helps in attributing costs to the Pipeline run as a whole.
However, for most non-Resource-based services, GCP cost data is not labeled by user or other information that would allow business categories to be separated out.
Here is a solution: Create multiple projects to serve as the Client. Google Cloud organizations commonly use many projects, organized in folders. These projects can then be set as the Client to allocate costs to business categories. The projects can be otherwise empty, and you don’t have to run the client code in the billing project — what matters is the Client project on the invocation. In this way, the projects themselves serve as the "label" designating the business unit.