Search Console API 403 or Missing Property: A Read-Only Debugging Guide
Debug Search Console API 403 errors and missing properties by checking Google identity, exact siteUrl, read-only OAuth scopes and empty responses.
A Search Console API 403 is not a reason to request wider permissions immediately. Check the error body, the Google identity behind the token, the exact property identifier and the granted OAuth scopes. List properties with that same credential, then run a minimal read-only query. A successful response without rows is a different debugging branch.
Start by identifying which layer failed
A property missing from an application can mean several things: Google did not return it, the application filtered it out, or the request used a different identifier. A 403 is an HTTP failure, not a synonym for any of those situations.
Keep four facts together: the operation, the authenticated identity, the literal siteUrl and the response status. Google’s standard error documentation describes structured error fields including reason and message. Inspect those fields before deciding whether the problem belongs to authorization, request configuration or another error category.
Do not begin by deleting and recreating properties, adding owners or switching credentials repeatedly. Those changes destroy useful comparisons. For a broader introduction after the immediate diagnosis, use the Search Console API guide.
Use this read-only diagnostic decision table
The table is a debugging order, not a promise that every service uses identical error wording. Use the actual response to choose the next check.
| Observation | Read-only check | Next decision |
|---|---|---|
| HTTP 401 | Check token availability and expiry through the existing OAuth client | Refresh or reauthorize through the supported flow; do not change property roles first |
| HTTP 403 | Capture the sanitized reason and message, operation and project context | Investigate the layer named by the error, not just the status |
Property absent from sites.list | Confirm the token’s Google identity and the intended property in Search Console | Ask an owner to inspect that identity’s access if it is the correct account |
| Property listed, query fails | Copy its exact siteUrl; check encoding and granted scopes | Correct the request or authorization without widening permissions blindly |
| Google lists it, application does not | Compare the raw list with application selection and filtering | Investigate application state rather than changing Google’s property |
| HTTP 200, no rows | Remove filters and use completed dates with known activity | Investigate data selection separately from access |
Stop at the first unresolved boundary. If listing itself fails, changing Search Analytics dimensions cannot explain that failure. If listing succeeds, preserve that successful baseline while testing the query.
Confirm the Google account, not just the browser session
The Google account active in a browser tab is not proof of which identity authorized a background application. A developer can view a client’s property under one account while a scheduled job uses a connection created under another.
Check your application’s authorized-account record without exposing credentials. Then open Search Console under that same identity and select the intended property. If access is missing, ask the property owner to inspect the exact account in Settings > Users and permissions.
Google’s permissions documentation distinguishes owners, full users and restricted users, and explains that owners manage other users’ access. Property rights are separate from an OAuth application’s scopes. Granting consent to an application does not create property ownership.
Avoid asking for owner access as a default troubleshooting step. First establish whether the identity already has the access needed for the read operation. For agency workflows, keep an account-to-property register like the one described in connecting multiple client sites.
Copy the exact Domain or URL-prefix identifier
Google’s Search Analytics method reference defines siteUrl as the property URL as defined in Search Console. It explicitly gives both URL-prefix and Domain examples:
- Domain property:
sc-domain:example.com - URL-prefix property:
https://www.example.com/
Do not turn the Domain identifier into https://example.com/, strip www from a returned URL-prefix identifier, or assume HTTP and HTTPS properties are interchangeable. Copy the identifier returned for the intended property instead of building it from the company’s homepage URL.
The Sites resource returns siteUrl and permissionLevel. Its permission values include siteOwner, siteFullUser, siteRestrictedUser and siteUnverifiedUser. A list entry is evidence to inspect, not permission to ignore its reported level.
For a raw HTTP request, encode the property identifier as one path component. A client library may handle this for you; follow its interface instead of pre-encoding a value it expects to receive literally. Double encoding can make the server receive a different identifier.
Verify granted OAuth scopes without escalating them
Google documents https://www.googleapis.com/auth/webmasters.readonly as an accepted scope for both listing properties and querying Search Analytics. The broader webmasters scope is read/write, as Google’s authorization guide explains. It is not necessary just to perform these reads.
Inspect what the application was actually granted, not only what its source code currently requests. Google’s OAuth overview instructs applications to compare granted scopes with required scopes. Changing a scope string in configuration does not prove that an older stored authorization now includes it.
If the read-only scope is missing, use the application’s supported consent flow for the required read access. If the error instead names API configuration, verify the Search Console API configuration for the relevant project. An API key alone does not authorize these private user-data requests.
Keep these questions separate: can the token authorize the method, and can its identity access this property? A broader scope cannot repair missing property access. Repeatedly requesting write access only increases the grant without establishing the cause.
Run a minimal read-only request pair
These are HTTP request templates, not a login script or a tested connection to your property. Substitute a short-lived token locally through your existing authorized client. Never paste a real token into an issue, chat or committed file. The first request has no body, as the list reference specifies.
GET /webmasters/v3/sites HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer REDACTED_ACCESS_TOKEN
Read the returned entries and select the literal identifier. For the example Domain property sc-domain:example.com, its encoded path component is sc-domain%3Aexample.com. The following request reads performance data even though the HTTP method is POST:
POST /webmasters/v3/sites/sc-domain%3Aexample.com/searchAnalytics/query HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer REDACTED_ACCESS_TOKEN
Content-Type: application/json
{
"startDate": "2026-10-01",
"endDate": "2026-10-07",
"type": "web",
"dimensions": ["date"],
"rowLimit": 10
}
Replace the illustrative dates with a completed period for which the property has known activity. This small query has no page, country or query filters. It is designed to isolate access and request construction, not to export a complete dataset or reproduce a dashboard.
Keep the credential unchanged between the two requests. Otherwise, a successful list and failed query might describe two different accounts rather than one reproducible problem.
Treat zero rows as a data question, not another 403
A non-success HTTP response and a successful response without rows must remain different states in your code. Do not catch every exception and return an empty array; that makes a broken connection look like a quiet week.
The Search Analytics reference says that days without data are omitted when grouping by date. It also describes inclusive date boundaries, Pacific Time dates and the API’s limits on returning rows. An absent day is not automatically a permission failure, and a small result is not proof of complete coverage.
For a successful empty result, check the exact property again, use completed dates, remove dimension filters and confirm the search type. Compare the same period and search type in Search Console under the same account. Add filters back one at a time once a simple query returns data.
Historical availability is another separate constraint. If the requested period is old, consult the Search Console history guide rather than repeatedly reconnecting an account. Reauthorization cannot create historical rows that are unavailable.
Keep a redacted error trail and a clear stop condition
Use this workflow in a local diagnostic run or a restricted support record:
- Record the timestamp, operation and HTTP status before transforming the response.
- Extract the error reason and a reviewed, sanitized message if present. Preserve the original structure privately when policy permits.
- Replace client domains and account addresses with consistent aliases before sharing. Remove authorization headers, access and refresh tokens, client secrets and cookies entirely.
- Note whether the same credential could list the target, its reported permission level and whether the query used that exact identifier.
- Change one variable, repeat the same minimal request, and record whether the failure changed.
An illustrative record might contain operation=searchanalytics.query, http_status=403, property_alias=client-a-domain and reason=REDACTED_REASON_FROM_RESPONSE. These are placeholders, not an observed Google response. Do not invent a reason when the error body does not provide one.
Stop when you can identify the failing boundary and assign it to the right person: the application maintainer for request construction, the connection owner for consent, or the property owner for account access. If escalation is needed, share the sanitized reproduction, not a credential bundle.
Frequently asked questions
Does a Search Console API 403 always mean missing property access?
No. Read the error reason and message. Property access, OAuth scopes and API configuration are separate checks; the HTTP status alone does not identify the failing layer.
Which property identifier should I send to the API?
Use the exact siteUrl returned for the intended property, such as sc-domain:example.com or https://www.example.com/. Domain and URL-prefix identifiers are not interchangeable.
Do I need the read/write webmasters scope to query performance?
No. Google documents webmasters.readonly as sufficient for both sites.list and searchanalytics.query. A broader scope does not grant access to a property the Google identity cannot access.
Why can I see a property in my browser but not in my application?
First compare the Google identity selected in Search Console with the identity that authorized the application. Then compare the exact property identifier and the application’s granted scopes.
Is an empty Search Analytics response a permission error?
Not by itself. A successful response with no rows can reflect the date range, search type or filters. Keep that result separate from a failed HTTP request and test a simpler query.
Once the connection is understood, create a GSC Magic account to continue your Search Console workflow.
Give your agent your search data.
GSC Magic connects Claude, ChatGPT and Cursor to Google Search Console, Bing and GA4. Free plan, 7-day Pro trial, no card.