When the Google Ads API returns RESOURCE_EXHAUSTED with a rate scope of DEVELOPER, the cause is almost always one of five things: your Google Cloud project hit its daily operation limit for its access level, a script ran large validateOnly dry runs that spent the quota the same as real changes, a mutate job sent hundreds of individual operations that each counted, reports re-ran the same searches instead of reading a cache, or conversion uploads share the same project as everything else. You confirm which one from the quotaErrorDetails in the error, the access level in Google Cloud Console, and your own logs for the last 24 hours. The fix is to validate once and cache the result, cut repeated searches, give critical uploads their own project and schedule, and make reports fall back to a stored snapshot.
The Google Ads API meters usage in operations. A Search or SearchStream request is one operation, every individual operation inside a mutate request is one operation, and any other request (a ConversionUploadService.UploadClickConversions call, for example) is one operation. RESOURCE_EXHAUSTED is the QuotaError that means "too many requests" against that daily allowance. RESOURCE_TEMPORARILY_EXHAUSTED is a different QuotaError that means too many requests in a short amount of time, a per second rate limit you recover from in seconds, not hours.
Is the quota tied to the developer token or the Google Cloud project?
As of October 2026, Google Ads API access levels and daily quotas belong to the Google Cloud project that owns your OAuth credentials, not to the developer token. Google sunset developer tokens on September 9, 2026 and transferred each active token's access level to the Cloud projects that used it. You can still send the developer-token header, but Google documents that it is optional and ignored. The QuotaRateScope enum now describes DEVELOPER as a per project quota, formerly the developer token quota. So when the error says rateScope DEVELOPER, read it as "this Cloud project is out of operations". ACCOUNT scope means a single customer account is being rate limited.
So a second developer token no longer buys a separate quota. A second Cloud project, with its own OAuth client or service account and access level, does.
The five causes we find most often.
We hit this twice on our own tooling, both times with a lockout that lasted many hours, and both times the trigger was a change set, not a report.
- Daily operation limit reached: the project sits at Explorer or Basic access and the last 24 hours of searches, mutate operations and uploads crossed 2,880 or 15,000 operations
- validateOnly dry runs counted like real changes: in our accounts, validating a change set of several hundred operations (new ad groups with keywords and responsive search ads) consumed quota the same way the real mutate would, and running the dry run several times burned through the day
- Large mutates counted per operation: one request carrying 400 keyword operations is 400 operations against the daily limit, not one
- Reports re-running the same searches: dashboards and agents querying the same customers on every refresh, with nothing cached
- Shared project for everything: uploads, reporting and change scripts use one Cloud project, so when one job spends the quota, the uploads stop too
How do you tell which quota you hit?
Read the quotaErrorDetails inside the error before you retry anything: rateScope tells you project or account, rateName names the bucket, and retryDelay tells you how long to wait. Google documents quota_error_details as carrying the scope (account or developer), the rate bucket name and the retry delay. In REST it sits in the GoogleAdsFailure under error.details.
The error payload. Log the full failure, not just the message. RESOURCE_EXHAUSTED with rateScope DEVELOPER is the daily project quota. RESOURCE_TEMPORARILY_EXHAUSTED is the short term rate limit. In our lockouts the retryDelay ran to hours, which is the tell that backoff in a loop will not help.
The Google Ads API Overview page in Google Cloud Console. Open the Cloud project that owns the OAuth client or service account your script uses and read its current access level. Test, Explorer, Basic and Standard each have a different ceiling.
Your own request log. Count operations, not requests, for the last 24 hours: one per search, one per mutate operation, one per upload or other call. Google measures "per day" as a sliding 24 hour window, so a burst at 3 p.m. yesterday still counts at 2 p.m. today.
What are the daily operation limits for each access level?
Explorer access allows 2,880 operations per day against production accounts, Basic allows 15,000, and Standard is unlimited. Test access is limited to test accounts at 15,000 operations per day. Explorer also blocks some services outright, including Keyword Planner and the other planning services.
| Access level | Accounts it can touch | Daily operation limit | How you get it |
|---|---|---|---|
| Test | Test accounts only | 15,000 per day | Granted when you enable the API in a Cloud project |
| Explorer | Test and production | 2,880 per day on production, 15,000 on test | Apply from the Google Ads API Overview page, may be approved automatically |
| Basic | Test and production | 15,000 per day on both | Apply after brand verification of the Cloud project |
| Standard | Test and production | Unlimited daily operations | Manual review, typically about 10 business days |
Standard removes the daily ceiling but not rate limits, which still return RESOURCE_TEMPORARILY_EXHAUSTED.
Does validateOnly count against the daily quota?
Treat every validateOnly request as if it spends the same operations as the real request. Google's quota page does not exempt validate_only, and it does say that requests rejected with a GoogleAdsFailure still count against the daily operation quota. In our own accounts, dry runs of large change sets counted operation for operation, which is how a cautious "check it first" habit locked us out. Validate once per change set, store the result with a hash of the operations, and only re-validate the parts that changed.
Diagnose top to bottom.
Each step isolates one layer. Stop at the first real failure.
Symptom, cause and fix.
| Symptom | Likely cause | Fix |
|---|---|---|
| RESOURCE_EXHAUSTED, rateScope DEVELOPER, retryDelay in hours | Daily operation limit for the project's access level | Stop retries, find the spending job, wait out the window, apply for a higher access level |
| Quota gone after a dry run, nothing changed in the account | validateOnly runs of a large change set | Validate once, cache the result, re-validate only changed operations |
| Quota gone after one bulk build | Each mutate operation counts individually | Spread large builds across days or move to Basic or Standard access |
| RESOURCE_TEMPORARILY_EXHAUSTED, clears in seconds | Rate limit on requests or operations per minute | Exponential backoff with jitter, cap concurrent tasks, client side rate limiter |
| Offline conversion uploads failing at the same time | Uploads share the Cloud project with change scripts | Run uploads from their own project and schedule, batch up to 2,000 conversions per request |
| Dashboards blank during the lockout | Reports query the API live on every load | Serve the last stored snapshot when the API errors |
| PAGE_SIZE_NOT_SUPPORTED on googleAds:search | page_size set in the request | Remove page_size, the page size is fixed at 10,000 rows |
| Keyword Planner calls rejected on a new project | Explorer access blocks planning services | Apply for Basic or Standard access |
How do you stop it from happening again?
Batching operations into fewer requests helps with rate limits, but it does not reduce the daily operation count for mutates. Google counts each mutate operation, so 400 operations in one request and 400 in forty requests cost the same against the day. What does reduce the count:
- Validate once: hash the change set, store the validation result, and skip the dry run when the hash has not changed
- Cache reads: store search results with a timestamp and let dashboards and agents read the cache, refreshing on a schedule instead of on every load
- Use fewer, wider queries: one SearchStream request is one operation no matter how many batches it streams, and paged Search requests with a valid next_page_token do not count again
- Split critical work by project: conversion uploads on one Cloud project and schedule, bulk changes on another, so a big build cannot starve the uploads
- Upload in bulk: an UploadClickConversions call counts as one operation and can carry up to 2,000 conversions, so batch rows instead of sending one call per lead
- Fail soft: on RESOURCE_EXHAUSTED, show the last snapshot with its age
We apply the same rule to every quota limited Google API we run, including the Indexing API: count units before you schedule a job, and never retry against a daily cap.
Why does pageSize fail on googleAds:search?
Google Ads API v19 and later fix the Search page size at 10,000 rows and return PAGE_SIZE_NOT_SUPPORTED if you set page_size. We hit this calling googleAds:search on v22 with code written for older versions. Remove the field and page with next_page_token. Paged requests with a valid token do not count again; an expired or invalid token does.
What is normal and what is a red flag.
Normal: an occasional RESOURCE_TEMPORARILY_EXHAUSTED during a burst that clears after backoff, since Google meters short term rates with a token bucket. Normal: a project at Explorer access running out on a heavy build day, since 2,880 operations is roughly two per minute averaged over 24 hours.
Red flags: RESOURCE_EXHAUSTED with rateScope DEVELOPER on a project that should be nowhere near its limit, which usually means a forgotten scheduler or a retry loop. Any job that retries a daily quota error on a timer. Conversion uploads running on the same project as bulk changes. A dashboard that goes blank instead of showing cached data. After a lockout, run our conversion tracking audit checklist to confirm nothing downstream broke. On Microsoft, a different API failure is covered in Microsoft Ads API settings not saving.
What does RESOURCE_EXHAUSTED mean in the Google Ads API?
It is the QuotaError for too many requests against a quota. With rateScope DEVELOPER it means your Google Cloud project has used its daily operation limit for its access level, measured over a sliding 24 hour window. Read retryDelay in quotaErrorDetails to see how long to wait.
How many operations per day does the Google Ads API allow?
Test access allows 15,000 operations per day against test accounts. Explorer allows 2,880 per day against production accounts and 15,000 against test accounts. Basic allows 15,000 per day on both. Standard is unlimited, though rate limits still apply.
Do validateOnly requests count against the Google Ads API quota?
Google does not document an exemption, and requests rejected with a GoogleAdsFailure still count. In our accounts, validateOnly dry runs of large change sets counted the same as real mutates. Validate once per change set and cache the result.
What is the difference between RESOURCE_EXHAUSTED and RESOURCE_TEMPORARILY_EXHAUSTED?
RESOURCE_TEMPORARILY_EXHAUSTED means too many requests in a short time and clears with exponential backoff. RESOURCE_EXHAUSTED with DEVELOPER scope is the daily operation quota and does not clear until older operations roll out of the 24 hour window.
Will a new developer token give me more Google Ads API quota?
No. Google sunset developer tokens on September 9, 2026, and access levels now belong to the Google Cloud project that owns your OAuth credentials. A separate Cloud project with its own access level has its own quota.
Why does the Google Ads API reject page_size?
From v19 onward the Search page size is fixed at 10,000 rows, and setting page_size returns PAGE_SIZE_NOT_SUPPORTED. Remove the field and page with next_page_token.