Connecting GA4, Search Console, and Google Ads
SignalGuide reads three Google sources: Google Analytics 4, Search Console, and (soon) Google Ads. Access is read-only. One consent screen covers all of them. This page is the reference — for the quick walkthrough, see onboarding.
The one connection
You sign in with Google once. A single consent screen requests every scope SignalGuide needs — there is no per-service reconnect. The scope list lives in api/services/google_auth.py:
What each scope is for:
analytics.readonly— list GA4 properties and pull page, session, user, bounce, channel, and referral data. Read-only.webmasters.readonly— list verified Search Console sites and pull query, page, impression, click, and position data. Read-only.adwords— reserved for the upcoming Google Ads integration. Currently unused at the data layer.userinfo.email/userinfo.profile— used once to create your SignalGuide account from your Google identity.
The OAuth flow requests access_type=offline with prompt=consent, so Google issues a refresh token and SignalGuide can pull data on your schedule without re-prompting.
Google Analytics 4
GA4 is where SignalGuide gets traffic volume, engagement, and channel-of-origin. The data queries live in api/services/ga4.py.
What we pull
- Page-level metrics by
pagePath: page views, sessions, bounce rate, total users (top 500 pages per period). - Site totals: total users, new users, sessions, page views. Pulled as a separate query because unique users are not additive across pages.
- Source funnels by
sessionDefaultChannelGroup: sessions and conversion-page landings per channel, used to compute per-channel conversion rates against your configured conversion pages. - AI referrals by
sessionSource: ChatGPT, Gemini, Perplexity, Claude, Copilot, and You.com are detected as first-class sources. The domain list is inAI_SOURCE_MAPinga4.py. - Per-page daily time series, used by the page drilldown drawer.
Picking a property
After you connect Google, GET /properties/ga4 calls the GA4 Admin API and returns every property your Google account can see (see list_ga4_properties in api/services/ga4.py and api/routes/properties.py). You pick one in the onboarding flow; it gets stored on the site row as ga4_property_id.
To change the property later, edit the site from the Sites page (/sites) — the update hits PUT /sites/{site_id} in api/routes/sites.py and swaps the stored property ID. No re-auth needed unless you switched Google accounts.
Permission required
Your Google account needs at least Viewer access on the GA4 property. The code uses analytics.readonly, which is the lowest-privilege scope Google offers for the Data and Admin APIs — Viewer is enough for every query SignalGuide runs. If a property you own doesn't appear in the picker, check property access in GA4 Admin.
Search Console
Search Console is where SignalGuide gets what you rank for — queries, pages, impressions, clicks, and average position. The queries live in api/services/gsc.py.
What we pull
- Page-dimension search performance: path, impressions, clicks, position, CTR (top 500 pages per period).
- Query-dimension search performance: query string, impressions, clicks, position, CTR (top 500 queries).
- Query × page rows up to
rowLimit=5000— the raw material for striking-distance analysis ("which query is this page ranking for and at what position?").
Picking a site
GET /properties/gsc calls searchconsole.sites().list() and returns every verified site on your Google account (see list_gsc_sites in api/services/gsc.py). You pick one during onboarding and it's stored as gsc_site_url on the site row.
sc-domain: is a Domain property (covers every subdomain and protocol). Anything else is a URL-prefix property (only the exact origin). If a URL-prefix site shows lower numbers than you expect, you're probably missing www or https variants — the Domain property is cleaner.If your site doesn't appear
The GSC API only returns sites where your Google account is a verified owner or user. If you connected the wrong Google account, or you haven't verified the site in Search Console yet, it won't show up in the picker. Verify at search.google.com/search-console, then reload the onboarding flow.
Google Ads
Google Ads is in development. The adwords scope is requested in the initial consent so no second authorization is needed when it ships.
GET /properties/ads, ads data pulls) are wired as stubs today — see api/services/ads.py, which returns empty lists and logs "Phase 2." The landing page notes the same: "Google Ads coming soon." When it launches, SignalGuide will correlate spend, conversions, and keywords against GA4 channel data and GSC rankings. No re-auth will be required.What we don't do
- We can't change your Google data. Every data scope we request is
.readonly. SignalGuide has no write permission on GA4, GSC, or (when it launches) Ads. - We don't share or sell your data. Analysis results are stored in your SignalGuide account and used only to generate your reports.
- Snapshots are kept until you delete them. There is no automatic TTL on analysis snapshots — deleting a site (
DELETE /sites/{site_id}inapi/routes/sites.py) cascades and removes the underlying snapshot data with it.
Switching properties or disconnecting
Change the connected GA4 property or GSC site: open the site from /sites and edit it. The frontend calls PUT /sites/{site_id}; only the fields you send are updated (see SiteUpdate in api/routes/sites.py).
Remove a site entirely: DELETE /sites/{site_id} deletes the site row and its associated data. Your Google tokens on the user account are untouched, so other sites under the same account keep working.
Revoke SignalGuide's access to your Google account: go to myaccount.google.com/permissions and remove SignalGuide. After that, token refresh will fail and no further data pulls will succeed. There is no in-product revoke button today.
Troubleshooting
401 or "expired token"
SignalGuide refreshes the access token automatically using your stored refresh token (refresh_access_token in api/services/google_auth.py). If the refresh itself fails — usually because you revoked access at myaccount.google.com, or Google rotated the grant — the server logs "Token refresh failed" and pulls return empty. Fix it by signing in with Google again from the login screen.
A GA4 property doesn't appear
list_ga4_properties only returns properties visible to the Google account you connected. Most often this is a permission issue: add your Google identity as a Viewer on the property in GA4 Admin, then reload the property picker. If you connected the wrong Google account, revoke at myaccount.google.com/permissions and reconnect.
A Search Console site doesn't appear
The site must be verified in Search Console under the Google account you connected. list_gsc_sites reads from Google's site list — if GSC doesn't know about it, neither do we. Verify the site at search.google.com/search-console first.
Analysis returns empty
The analysis service refuses to persist empty snapshots — see api/services/analysis.py. If a run comes back with zero pages and zero impressions, it's almost always an expired token, a revoked scope, or a transient API failure. Re-auth and rerun.