Google Search Console
Import Google Search clicks, impressions, CTR and average position for your sites and see them next to pages and sources in Analytics.
The Search Console integration imports Google Search performance for the sites of a project once a day: clicks, impressions, click-through rate (CTR) and average position, per search query and per page. Analytics shows them next to your own pageviews. A project manager connects one Google account per project through OAuth, then picks the Search Console property for each site.
The integration is off until the instance has a Google OAuth client. Without one, the Search Console settings page says "Search Console isn't configured on this instance" and offers no Connect button. See Self-hosting.
Connect
You need to be a project manager (an organization admin).
- Open the project's settings and choose Search Console (
/o/:org/projects/:project/settings/search-console). - Click Connect Google. Google asks you to let Unhandled view your Search Console data. That is the only permission it asks for:
https://www.googleapis.com/auth/webmasters.readonly. - Google sends you back to the settings page, which lists every site that sent analytics in the last 30 days.
- Pick the Search Console property for each site, such as
sc-domain:shop.exampleorhttps://shop.example/. Only properties the Google account can read are offered.
The connect link carries an encrypted state that expires after 10 minutes and only completes for the manager who started it. Unhandled stores the Google access and refresh tokens encrypted.
Imports
- The first import reaches back 16 months, which is all Search Console keeps, or as far as the organization keeps analytics data, whichever is shorter. It runs in slices of 14 days, so a full backfill finishes over a few hours without bursting Google's quota.
- After that, each site imports the newly final day once a day. Google publishes a day as final about three days after it ends, so the three newest days of any range are empty.
- An organization's imports run one at a time, at most 5 sites per run; the rest follow in the next run 5 minutes later. When Google answers that a quota is exhausted (HTTP 429, or 403 with a quota reason), the site waits 15 minutes, doubling after every further failure up to 24 hours, or as long as Google's
Retry-Afterasks. - If Google revokes the grant, or refuses a freshly refreshed token, the connection shows Reconnect and imports stop until you connect again. Nothing retries a dead grant.
- Running a day's import twice never double counts it.
Changing a site's property deletes the data imported from the old one and starts over. Clearing it (the × button) stops the import and deletes that site's imported data. If Google stops letting the account read the property (403), the site's import pauses with that reason until you pick the property again or reconnect.
A domain property (sc-domain:shop.example) covers every subdomain. Page rows are kept only for the linked site's own host (www. is ignored), so blog.shop.example pages never count toward shop.example. Google reports a domain property's site totals and queries across all its hosts, so those include other subdomains.
Where the data shows
- Pages: Search clicks, Impressions, CTR and Position columns join each route or URL by its path. Search Console reports full URLs, so
https://shop.example/docs/42counts toward/docs/42. Under Routes, a path takes the route your app declared for it (therouteoption orunhandled-routemeta) when analytics saw that path in the range, otherwise the computed template such as/docs/:id. The columns cover the whole range and site: analytics filters and the comparison do not apply to them. - Overview: in the Sources card, a
google.*row expands to the top Google search queries of the range with their clicks, impressions, CTR and position. - MCP: the
get_search_consoletool of the MCP server returns the totals, the top 50 queries and the pages.
Totals come from Google's own aggregates for the site and for each page. Google leaves rare queries out of query rows to protect searchers' privacy, so the queries add up to less than the totals. Position is Google's average position, weighted by impressions across days and pages.
Privacy
Search queries are third-party data you chose to import. They are rendered as plain text and listed as untrusted fields in MCP results. Imported data follows the organization's analytics retention and is removed with the project or organization. Disconnect revokes Google's access, stops imports and deletes the project's imported data.
Self-hosting
An instance owner creates the Google OAuth client once:
- In the Google Cloud console, create or pick a project.
- Under APIs & Services > Library, enable the Google Search Console API.
- Under APIs & Services > OAuth consent screen, configure the consent screen: user type External (or Internal for a Google Workspace-only instance), app name and support email, and add the scope
https://www.googleapis.com/auth/webmasters.readonlyand nothing else. Publish the app, or add the people who will connect as test users while it is in testing. - Under APIs & Services > Credentials, create an OAuth client ID of type Web application, and add the authorized redirect URI
https://unhandled.example.com/integrations/google/callback, with your instance'sAPP_URLin place ofhttps://unhandled.example.com. - Set the client's ID and secret on the web and worker processes:
| Variable | Value |
|---|---|
GOOGLE_OAUTH_CLIENT_ID | The OAuth client's ID |
GOOGLE_OAUTH_CLIENT_SECRET | The OAuth client's secret |
Both are optional. While either is unset, Search Console stays off and nothing calls Google. Disconnecting still deletes a project's imported data.
webmasters.readonly is a sensitive scope, so an app in production status needs Google's verification before people outside your organization can connect without an "unverified app" warning. While the consent screen is in Testing status, only listed test users can connect, and Google expires their refresh tokens after 7 days; the connection then shows Reconnect. Publish the app for connections that last.