[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"post:\u002Fblog\u002Fsearch-console-api-service-account-403-errors":3,"blog-order":446},{"page":4,"collection":445},{"id":5,"title":6,"author":7,"body":8,"bottomLine":7,"cluster":7,"cta":7,"date":428,"definition":7,"description":429,"draft":430,"extension":431,"factChecker":7,"faq":7,"image":432,"meta":433,"meta_description":438,"meta_title":7,"navigation":439,"path":440,"related":7,"reviewer":7,"seo":441,"sitemap":442,"sources":7,"stem":443,"summary":7,"takeaways":7,"term":7,"tier":7,"updated":7,"__hash__":444},"posts\u002Fblog\u002Fsearch-console-api-service-account-403-errors.md","Search Console API: Service Account Gets 403 Errors",null,{"type":9,"value":10,"toc":402},"minimark",[11,19,22,55,63,70,75,81,86,105,112,116,119,130,144,148,154,165,171,175,184,196,199,206,210,224,227,231,234,238,241,256,260,281,288,292,299,303,315,322,326,329,335,343,347,351,362,365,369,378,385,389,396],[12,13,14,18],"p",{},[15,16,17],"strong",{},"A Search Console API 403 doesn’t always mean you need more permissions."," I start with the error reason, then check which identity sent the request. Cloud IAM roles do not grant Search Console property access.",[12,20,21],{},"Here’s my checklist:",[23,24,25,32,43,49],"ul",{},[26,27,28,31],"li",{},[15,29,30],{},"Identity:"," Confirm the deployed job’s service account or impersonated user - not just your local credentials.",[26,33,34,37,38,42],{},[15,35,36],{},"Property access:"," Match the exact property ID, such as ",[39,40,41],"code",{},"sc-domain:example.com",", and use the lowest role the method allows.",[26,44,45,48],{},[15,46,47],{},"API and scopes:"," Check the consumer project’s API status and the token’s read or write scope.",[26,50,51,54],{},[15,52,53],{},"Test and record:"," Retry a small, 7-day read-only query, then save the working settings without keys or tokens.",[12,56,57,58,62],{},"I change ",[59,60,61],"em",{},"one setting at a time",". A successful read test confirms read access - not permission to write.",[12,64,65],{},[66,67],"img",{"alt":68,"src":69},"Search Console API 403 Troubleshooting Flow","\u002Fuploads\u002F2026\u002F11\u002Fsearch-console-api-service-account-403-errors-599e3874.jpg",[71,72,74],"h2",{"id":73},"confirm-which-identity-sends-the-request","Confirm Which Identity Sends the Request",[12,76,77,80],{},[15,78,79],{},"Check the deployed job, not just your local setup."," A passing local test doesn’t prove production uses the same credentials. Inspect the job’s environment variables, mounted secrets, secret versions, and runtime service account.",[82,83,85],"h3",{"id":84},"check-active-credentials-and-settings","Check Active Credentials and Settings",[12,87,88,89,96,97,100,101,104],{},"If the property and role don’t explain the 403, check which identity production uses. For tools using ",[90,91,95],"a",{"href":92,"rel":93},"https:\u002F\u002Fdocs.cloud.google.com\u002Fdocs\u002Fauthentication\u002Fapplication-default-credentials",[94],"nofollow","Application Default Credentials"," (ADC), ADC checks ",[39,98,99],{},"GOOGLE_APPLICATION_CREDENTIALS"," first, then credentials from ",[39,102,103],{},"gcloud auth application-default login",", and finally the runtime’s service account.",[12,106,107,108,111],{},"Compare production with your local setup: identity, credential source, credential project, requested scopes, and exact property ID. Check workload identity settings where they apply. ",[15,109,110],{},"Log identity metadata, not credential contents."," Use the authentication library and runtime configuration to confirm the effective principal without exposing keys or tokens.",[82,113,115],{"id":114},"check-impersonation-and-delegated-access","Check Impersonation and Delegated Access",[12,117,118],{},"If the request uses a derived identity, check the caller and target separately.",[12,120,121,122,125,126,129],{},"For ",[15,123,124],{},"service-account impersonation",", confirm both permissions: the caller can mint the target token (",[39,127,128],{},"iam.serviceAccounts.getAccessToken","), and the target service account has Search Console access.",[12,131,121,132,135,136,139,140,143],{},[15,133,134],{},"domain-wide delegation",", confirm that the ",[39,137,138],{},"subject"," user has property access. Also check that the Workspace admin approved the required OAuth scopes for the service account’s ",[15,141,142],{},"client ID, not its email",".",[71,145,147],{"id":146},"check-access-to-the-exact-search-console-property","Check Access to the Exact Search Console Property",[12,149,150,151,143],{},"Once you’ve identified who’s sending the request, check that identity’s access to the ",[15,152,153],{},"exact property being queried",[12,155,156,157,160,161,164],{},"Open that property in Search Console and go to ",[15,158,159],{},"Settings → Users and permissions",". For direct service-account access, look for the service account’s exact ",[39,162,163],{},"client_email",". If it isn’t listed, the owner must add it.",[12,166,167,170],{},[15,168,169],{},"Cloud IAM does not grant Search Console property access."," Even a service account with broad Cloud roles can get a 403 if it hasn’t been added to the requested property.",[82,172,174],{"id":173},"match-the-domain-or-url-prefix-property-identifier","Match the Domain or URL-Prefix Property Identifier",[12,176,177,178,183],{},"The request’s ",[15,179,180],{},[39,181,182],{},"siteUrl"," must match the registered property exactly.",[185,186,187],"blockquote",{},[12,188,189,190,192,193,143],{},"A Domain property uses ",[39,191,41],{},"; a URL-prefix property uses a value such as ",[39,194,195],{},"https:\u002F\u002Fwww.example.com\u002F",[12,197,198],{},"Check the protocol, hostname, path, and trailing slash where applicable.",[12,200,201,202,205],{},"Check ",[15,203,204],{},"every property the report requests",". Don’t assume access to a Domain property also grants access to separately registered URL-prefix properties. Permission on one URL prefix also doesn’t automatically grant access to a property with a different protocol, subdomain, or path.",[82,207,209],{"id":208},"choose-the-minimum-required-property-role","Choose the Minimum Required Property Role",[12,211,212,213,216,217,220,221],{},"For read-only reporting, use ",[15,214,215],{},"Restricted user"," access where supported. Google documents read permission as sufficient for ",[39,218,219],{},"searchAnalytics.query",". Before granting more access for writes, check the specific method’s requirements. ",[15,222,223],{},"Do not default to Owner.",[12,225,226],{},"If you’ve confirmed access but still get a 403, check API enablement and OAuth scopes next, or troubleshoot other Search Console errors if the 403 persists.",[71,228,230],{"id":229},"check-api-enablement-and-oauth-scopes","Check API Enablement and OAuth Scopes",[12,232,233],{},"If you’ve confirmed property access but still get a 403, check the API project and token scopes.",[82,235,237],{"id":236},"enable-the-api-in-the-consumer-project","Enable the API in the Consumer Project",[12,239,240],{},"Look at the full error response to find the project used by the request. If it doesn’t name a project, start with the service account’s project. Also check the reporting tool’s runtime settings for a quota project.",[12,242,243,244,247,248,255],{},"In that project, open ",[15,245,246],{},"Google Cloud Console → APIs & Services → Library"," and confirm that ",[15,249,250],{},[90,251,254],{"href":252,"rel":253},"https:\u002F\u002Fdevelopers.google.com\u002Fwebmaster-tools",[94],"Google Search Console API"," is enabled. Record the project ID, API name, enablement status, and timestamp so another administrator can repeat the checks.",[82,257,259],{"id":258},"match-oauth-scopes-to-the-request","Match OAuth Scopes to the Request",[12,261,262,263,268,269,272,273,275,276,143],{},"Once you’ve confirmed the API is enabled, check that the reporting tool’s token scopes match the request. Use ",[15,264,265],{},[39,266,267],{},"https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fwebmasters.readonly"," for read-only requests, such as ",[39,270,271],{},"sites.list"," and ",[39,274,219],{},". For read\u002Fwrite access, such as managing sitemaps, use ",[15,277,278],{},[39,279,280],{},"https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fwebmasters",[12,282,283,284,287],{},"After changing scopes, reissue the scoped credentials and get a new access token. For a user-based connection, renew authorization. ",[15,285,286],{},"Changing the code won’t update an issued token",", so clear any cached copy.",[71,289,291],{"id":290},"conclusion-test-the-fix-before-rerunning-reports","Conclusion: Test the Fix Before Rerunning Reports",[12,293,294,295,298],{},"Once you’ve corrected the cause of the permission-related 403, ",[15,296,297],{},"test the fix"," before rerunning reports.",[82,300,302],{"id":301},"retry-a-small-read-only-request","Retry a Small Read-Only Request",[12,304,305,306,308,309,272,312,143],{},"Send one ",[39,307,219],{}," request to the same property for the last 7 days. Use ",[39,310,311],{},"dimensions: [\"date\"]",[39,313,314],{},"rowLimit: 10",[12,316,317,318,321],{},"A successful status with data - or an empty result - confirms ",[15,319,320],{},"read access only",". If a write operation failed, check the permissions for that endpoint separately.",[82,323,325],{"id":324},"document-the-working-report-settings","Document the Working Report Settings",[12,327,328],{},"Record the runtime identity, exact property identifier, consumer project ID, OAuth scopes, environment and connection method, and successful test parameters. Include the date in MM\u002FDD\u002FYYYY format and the time in 12-hour format with AM\u002FPM.",[12,330,331,332,143],{},"Save only the credential name or secret-manager reference - ",[15,333,334],{},"never private keys, tokens, or service-account JSON",[12,336,337,338,342],{},"If the test succeeds, rerun the report with the same settings.  If you notice performance drops after fixing the API, use a ",[90,339,341],{"href":340},"\u002Fkeyword-cannibalization-checker","keyword cannibalization checker"," to ensure multiple pages aren't competing for the same terms.",[71,344,346],{"id":345},"faqs","FAQs",[82,348,350],{"id":349},"how-can-i-distinguish-permission-errors-from-quota-related-403s","How can I distinguish permission errors from quota-related 403s?",[12,352,353,354,357,358,361],{},"A ",[15,355,356],{},"permission-related 403 response"," usually means the service account or other identity your tool uses can’t access the Search Console property, or the API isn’t enabled for your project. A ",[15,359,360],{},"quota-related 403"," means you’ve exceeded the usage limits for your account or project.",[12,363,364],{},"Before blaming a rate limit, check which identity your reporting tool uses, whether it has property access, whether the API is enabled, and which OAuth scopes the tool has been granted.",[82,366,368],{"id":367},"how-long-should-i-wait-after-changing-property-access","How long should I wait after changing property access?",[12,370,371,372,377],{},"The provided documentation doesn’t specify a required wait time after changing property access. First, check the ",[90,373,376],{"href":374,"rel":375},"https:\u002F\u002Fsearch.google.com\u002Fsearch-console\u002Fabout",[94],"Google Search Console"," property settings to confirm that the identity used by your tool or service account has the required permissions.",[12,379,380,381,384],{},"If ",[15,382,383],{},"403 errors"," persist, confirm that your OAuth scopes permit read-only or full access and that the API is enabled in your Google Cloud project. Then wait briefly for permission changes to take effect before retrying.",[82,386,388],{"id":387},"what-should-i-check-if-403-errors-are-intermittent","What should I check if 403 errors are intermittent?",[12,390,391,392,395],{},"If you’re seeing intermittent Google Search Console API 403 errors, check that your reporting tool uses the ",[15,393,394],{},"expected application identity and valid OAuth scopes",". Make sure API access is still enabled and permissions haven’t changed for the property you’re querying.",[12,397,398,399,143],{},"Check for rate limits set by Google or your service’s own protections, too. For temporary request issues, retry with ",[15,400,401],{},"exponential backoff",{"title":403,"searchDepth":404,"depth":404,"links":405},"",3,[406,411,415,419,423],{"id":73,"depth":407,"text":74,"children":408},2,[409,410],{"id":84,"depth":404,"text":85},{"id":114,"depth":404,"text":115},{"id":146,"depth":407,"text":147,"children":412},[413,414],{"id":173,"depth":404,"text":174},{"id":208,"depth":404,"text":209},{"id":229,"depth":407,"text":230,"children":416},[417,418],{"id":236,"depth":404,"text":237},{"id":258,"depth":404,"text":259},{"id":290,"depth":407,"text":291,"children":420},[421,422],{"id":301,"depth":404,"text":302},{"id":324,"depth":404,"text":325},{"id":345,"depth":407,"text":346,"children":424},[425,426,427],{"id":349,"depth":404,"text":350},{"id":367,"depth":404,"text":368},{"id":387,"depth":404,"text":388},"2026-11-10","A Search Console API 403 doesn’t always mean you need more permissions. I start with the error reason, then check which identity sent the request. Cloud IAM roles do not grant Search Console property access.",false,"md","\u002Fuploads\u002F2026\u002F11\u002Fsearch-console-api-service-account-403-errors-e84854d9.jpg",{"tags":434},[435,436,437],"Content Strategy","SEO","Technical SEO","Diagnose Search Console API 403s: verify calling identity, exact property access, API enablement, and OAuth scopes; test with a small read query.",true,"\u002Fblog\u002Fsearch-console-api-service-account-403-errors",{"title":6,"description":429},{"loc":440},"blog\u002Fsearch-console-api-service-account-403-errors","Kc9KjGcmtkue_7w-useLAjlK4wWcO_XTz65GL1k6F5E","posts",[447,451,454,457,460,461,464,468,472,476,480,483,487,490,494,498,502,506,510,514,518,522,526,530,534,538,542,546,550,554,558,561,564,568,571,575,579,583],{"path":448,"title":449,"date":450},"\u002Fblog\u002Fgoogle-search-console-add-users","How to Add Users to Google Search Console","2026-11-11",{"path":452,"title":453,"date":450},"\u002Fblog\u002Fhow-to-check-if-a-page-is-indexed","How to Check if a Page Is Indexed by Google (4 Ways)",{"path":455,"title":456,"date":450},"\u002Fblog\u002Fhow-to-use-google-search-console","How to Use Google Search Console for SEO: A Report-by-Report Guide",{"path":458,"title":459,"date":450},"\u002Fblog\u002Fsubmit-sitemap-to-google","How to Submit a Sitemap to Google (and Check It Was Read)",{"path":440,"title":6,"date":428},{"path":462,"title":463,"date":428},"\u002Fblog\u002Fsee-keywords-google-search-console","How to See Your Keywords in Google Search Console",{"path":465,"title":466,"date":467},"\u002Fblog\u002Fmobile-desktop-ctr-difference-by-position","How Mobile and Desktop CTR Differ by Position","2026-11-09",{"path":469,"title":470,"date":471},"\u002Fblog\u002Fserp-features-affect-ctr-similar-positions","3 SERP Features That Change CTR at Similar Positions","2026-11-08",{"path":473,"title":474,"date":475},"\u002Fblog\u002Fgoogle-search-console-domain-vs-url-prefix","Google Search Console: Domain vs URL Prefix","2026-11-07",{"path":477,"title":478,"date":479},"\u002Fblog\u002Fgoogle-search-console-api","Google Search Console API: What It Returns, Its Limits, How to Query It","2026-11-06",{"path":481,"title":482,"date":479},"\u002Fblog\u002Fkeep-search-console-data-beyond-16-months","How to Keep Search Console Data Beyond 16 Months",{"path":484,"title":485,"date":486},"\u002Fblog\u002Fgoogle-chooses-different-canonical-urls","Why Does Google Choose Different Canonical URLs?","2026-11-05",{"path":488,"title":489,"date":486},"\u002Fblog\u002Frequest-indexing-google-search-console","How to Request Indexing in Google Search Console (and When It Helps)",{"path":491,"title":492,"date":493},"\u002Fblog\u002Ftitle-tag-generator","Title Tag Generator","2026-11-04",{"path":495,"title":496,"date":497},"\u002Fblog\u002Fwhy-google-rewrites-meta-descriptions-copy-fixes","Why Google Rewrites Meta Descriptions: Copy Fixes","2026-10-24",{"path":499,"title":500,"date":501},"\u002Fblog\u002Fanonymized-queries-vs-search-console-row-limits","Anonymized Queries vs. Search Console Row Limits","2026-10-23",{"path":503,"title":504,"date":505},"\u002Fblog\u002Fseo-reporting-search-console-metrics-clients","SEO Reporting: 7 Search Console Metrics for Clients","2026-10-21",{"path":507,"title":508,"date":509},"\u002Fblog\u002Finternal-linking-support-underperforming-pages","Internal Linking: How to Support Underperforming Pages","2026-10-20",{"path":511,"title":512,"date":513},"\u002Fblog\u002Fprioritize-seo-fixes-potential-traffic-gain","How to Prioritize SEO Fixes by Potential Traffic Gain","2026-10-19",{"path":515,"title":516,"date":517},"\u002Fblog\u002Flow-ctr-find-improve-pages-losing-clicks","Low CTR: How to Find and Improve Pages Losing Clicks","2026-10-18",{"path":519,"title":520,"date":521},"\u002Fblog\u002Fcontent-refresh-find-pages-worth-updating","Content Refresh: How to Find Pages Worth Updating","2026-10-17",{"path":523,"title":524,"date":525},"\u002Fblog\u002Forganic-traffic-drop-search-console-checklist","Organic Traffic Drop: A Search Console Checklist","2026-10-16",{"path":527,"title":528,"date":529},"\u002Fblog\u002Fmerge-or-redirect-competing-seo-pages","Merge or Redirect: How to Handle Competing SEO Pages","2026-10-14",{"path":531,"title":532,"date":533},"\u002Fblog\u002Fkeyword-mapping-prevent-cannibalization","What Is Keyword Mapping? (and How It Prevents Cannibalization)","2026-08-08",{"path":535,"title":536,"date":537},"\u002Fblog\u002Fkeyword-cannibalization-examples","7 Real Keyword Cannibalization Examples (and the Fix)","2026-08-05",{"path":539,"title":540,"date":541},"\u002Fblog\u002Flow-hanging-fruit-seo","Low-Hanging Fruit SEO: What It Is and How to Win It","2026-08-01",{"path":543,"title":544,"date":545},"\u002Fblog\u002Fstriking-distance-keywords","Striking Distance Keywords: Find & Rank the Page-2 Wins","2026-07-29",{"path":547,"title":548,"date":549},"\u002Fblog\u002Fbest-keyword-cannibalization-tools","Best Keyword Cannibalization Tools (2026 Comparison)","2026-07-25",{"path":551,"title":552,"date":553},"\u002Fblog\u002Fhow-to-avoid-keyword-cannibalization","How to Avoid & Prevent Keyword Cannibalization","2026-07-22",{"path":555,"title":556,"date":557},"\u002Fblog\u002Fwhat-is-keyword-cannibalization","What Is Keyword Cannibalization (and When It's Actually Fine)","2026-07-16",{"path":559,"title":560,"date":557},"\u002Fblog\u002Fhow-to-check-keyword-cannibalization","How to Check for Keyword Cannibalization: A Free Audit",{"path":562,"title":563,"date":557},"\u002Fblog\u002Fkeyword-cannibalization-ahrefs-semrush","How to Check Keyword Cannibalization in Ahrefs & Semrush (and the Faster Way)",{"path":565,"title":566,"date":567},"\u002Fblog\u002Fhow-to-find-low-hanging-fruit-keywords","How to Find Low-Hanging Fruit Keywords in Search Console","2026-07-14",{"path":569,"title":570,"date":567},"\u002Fblog\u002Finternal-keyword-cannibalization","Internal Keyword Cannibalization — How to Spot Your Pages Competing With Each Other",{"path":572,"title":573,"date":574},"\u002Fblog\u002Fanalyze-seo-performance-with-google-search-console","How to Analyze Your SEO Performance in Google Search Console","2018-04-08",{"path":576,"title":577,"date":578},"\u002Fblog\u002Fhow-to-find-and-fix-keyword-cannibalization","How to Fix Keyword Cannibalization (5 Fixes + a Decision Table)","2018-01-24",{"path":580,"title":581,"date":582},"\u002Fblog\u002Fhow-to-easily-find-quick-wins-to-boost-your-traffic","SEO Quick Wins: The 15-Minute Search Console Playbook","2018-01-23",{"path":584,"title":585,"date":582},"\u002Fblog\u002Fhow-to-identify-your-top-performers-with-keylogs","How to Identify Your Top-Performing Pages (80\u002F20) in Search Console"]