Connect Retool to Google Search Console by creating a REST API Resource using a Google service account for OAuth authentication. Query the Search Analytics API to pull search queries, CTR, impressions, and indexing data into Retool dashboards — giving your SEO and content teams interactive search performance reports without needing Google Search Console access.
| Fact | Value |
|---|---|
| Tool | Google Search Console |
| Category | SEO |
| Method | REST API Resource |
| Difficulty | Intermediate |
| Time required | 40 minutes |
| Last updated | April 2026 |
Build an SEO Performance Dashboard with Google Search Console Data in Retool
Google Search Console shows how your website performs in Google Search — which queries drive traffic, which pages rank highest, and where there are indexing or Core Web Vitals issues. But GSC's native interface is optimized for individual site owners rather than teams. In agencies, content teams, or multi-site operations, giving everyone GSC access creates security issues, and GSC's export formats aren't ideal for operational workflows.
Retool connects to GSC's Search Analytics API to give your team custom dashboards built around their specific workflows. An SEO manager can see top queries by CTR and impressions with date comparisons in a single table. A content team can filter by page URL to see which articles are gaining or losing visibility. A technical SEO team can monitor index coverage across all site sections without logging into GSC.
The main integration challenge is authentication: Google's APIs require OAuth 2.0, and for server-side access without user interaction, you use a service account. The service account generates signed JWT tokens that exchange for short-lived access tokens. In Retool, you handle this with a JavaScript query that fetches a fresh token using the service account credentials, then use that token for GSC API calls. The setup is more involved than API-key-based integrations, but it's a standard pattern for all Google API integrations.
Integration method
Google Search Console connects to Retool through a REST API Resource using Google OAuth 2.0 with a service account for server-to-server authentication. The service account generates access tokens that Retool uses as Bearer tokens on Search Console API requests. All requests are proxied through Retool's backend, keeping service account credentials secure. You build queries against the Search Analytics API to pull performance data for dashboards.
Prerequisites
- A Google Cloud project with the Search Console API enabled (console.cloud.google.com → APIs & Services → Enable APIs)
- A Google service account with a JSON key file downloaded (Console → IAM → Service Accounts → Create)
- The service account email granted access to your Search Console property (GSC → Settings → Users and permissions → Add user)
- Your Search Console property URL (e.g., https://www.yoursite.com or sc-domain:yoursite.com)
- A Retool account with permission to create Resources and store configuration variables
Step-by-step guide
Set up a Google Service Account for API access
Google Search Console API requires OAuth 2.0 authentication. For server-side Retool integration (no user browser redirect), you use a service account. In Google Cloud Console (console.cloud.google.com), navigate to APIs & Services → Library and search for 'Google Search Console API'. Click Enable. Next, go to IAM & Admin → Service Accounts → Create Service Account. Name it something like 'retool-gsc-reader'. Grant it no project-level roles (it only needs GSC property access, not Cloud project access). Complete the creation. Click the service account you just created, navigate to the Keys tab, and click Add Key → Create New Key → JSON. Download the JSON file — it contains the service account email, private key ID, and private key. Now grant this service account access to your GSC property. Open Google Search Console → Settings → Users and permissions → Add user. Enter the service account email (looks like retool-gsc-reader@your-project.iam.gserviceaccount.com). Grant it 'Restricted' access (read-only is sufficient). Store the key JSON contents as a Retool configuration variable named GOOGLE_SERVICE_ACCOUNT_JSON (mark as Secret). You'll reference specific fields from it in the token generation query.
Pro tip: Grant the service account the minimum permission level needed — 'Restricted' in GSC gives read access to Search Analytics data without access to URL Inspection or other sensitive operations. Upgrade to 'Full' only if you need URL Inspection API access.
Expected result: The service account is created in Google Cloud, the JSON key file is downloaded, the service account is added as a Restricted user in your GSC property, and the JSON key is stored as a Retool Secret configuration variable.
Create a token generation query using the service account
Google service accounts use JWT-based authentication to obtain short-lived access tokens. You need a JavaScript query that constructs the JWT, signs it with the service account private key, exchanges it for an access token, and stores the token for use in GSC API queries. Create a REST API Resource named 'Google OAuth Token'. Set Base URL to https://oauth2.googleapis.com. Create a JavaScript query named getGSCToken that: 1. Parses the service account JSON from configuration variables 2. Constructs a JWT header and payload 3. Signs the JWT using the service account private key 4. POSTs to the Google OAuth token endpoint to exchange the JWT for an access token Store the resulting access_token in a Retool Variable named gscAccessToken. Google access tokens expire after 1 hour, so add a refresh mechanism. Since native JWT signing isn't trivial in Retool's JavaScript environment, the most reliable approach is to use a separate backend service or Cloud Function that takes the service account JSON and returns a fresh token. Alternatively, use Retool's built-in Google OAuth resource type if available for your Retool plan.
1// JavaScript query: getGSCToken2// POST to Google's token endpoint to exchange service account for access token3// Note: This requires the google-auth-library or similar JWT signing4// For Retool, the simplest approach is a lightweight proxy endpoint56// Option 1: If you have a simple token proxy endpoint:7const response = await fetch('https://your-token-proxy.yourcompany.com/gsc-token', {8 method: 'POST',9 headers: { 'Content-Type': 'application/json' },10 body: JSON.stringify({11 // Pass identifying info — the proxy holds the service account credentials12 property: 'https://www.yoursite.com'13 })14});15const tokenData = await response.json();16return tokenData.access_token;1718// Option 2: Use Retool's native Google Sheets resource (which handles OAuth)19// then reuse the access token for GSC API callsPro tip: The cleanest production approach is a minimal token proxy — a Cloud Function or small API endpoint that holds your service account JSON and returns fresh access tokens on request. This avoids complex JWT signing in Retool's JavaScript environment. Alternatively, check if your Retool plan supports Google OAuth resource types.
Expected result: The getGSCToken query successfully returns a Google access_token string. The token is stored in the gscAccessToken Variable and has a 3600-second expiry.
Create the Search Console REST API Resource and search analytics query
Create a REST API Resource named 'Google Search Console API'. Set Base URL to https://searchconsole.googleapis.com. Add a default header: Authorization = Bearer {{ gscAccessToken.value }}. This attaches the OAuth token to all GSC queries. The token comes from the Variable set by getGSCToken. Now create the main analytics query. Name it getSearchAnalytics. Set Method to POST, Path to /v1/sites/{{ encodeURIComponent(textInput_siteUrl.value) }}/searchAnalytics/query. The siteUrl must exactly match your GSC property — use the full URL like https://www.yoursite.com or sc-domain:yoursite.com for domain properties. URL-encoding is required because the URL contains slashes and colons. The query body specifies your analytics parameters: - startDate and endDate (YYYY-MM-DD) - dimensions: array of grouping dimensions (query, page, country, device, searchAppearance) - rowLimit: max rows (up to 25,000) - startRow: for pagination - dimensionFilterGroups: for filtering by dimension values Bind the results to a Retool Table. The response contains a rows array where each row has keys (dimension values) and metrics (clicks, impressions, ctr, position).
1// POST body for Search Analytics API query2{3 "startDate": "{{ moment(dateRange.value[0]).format('YYYY-MM-DD') }}",4 "endDate": "{{ moment(dateRange.value[1]).format('YYYY-MM-DD') }}",5 "dimensions": ["{{ dropdown_dimension.value || 'query' }}"],6 "rowLimit": 100,7 "startRow": {{ table_results.paginationOffset || 0 }},8 "dimensionFilterGroups": [9 {10 "filters": [11 {12 "dimension": "page",13 "operator": "contains",14 "expression": "{{ textInput_urlFilter.value || '' }}"15 }16 ]17 }18 ]19}Pro tip: Remove the dimensionFilterGroups from the body when textInput_urlFilter is empty — an empty filter string may cause unexpected results. Use Retool's conditional syntax: {{ textInput_urlFilter.value ? '[{"filters":[...]}]' : '[]' }} for the dimensionFilterGroups value.
Expected result: The getSearchAnalytics query returns a rows array with search analytics data. The response includes keys (dimension values like query strings or page URLs) and metrics (clicks, impressions, CTR, position).
Transform and display search analytics data
GSC's response format requires transformation before it's ready for Retool Tables. Each row has a keys array (dimension values) and separate metric fields. The transformer converts this into named-key objects. Create a JavaScript transformer for getSearchAnalytics. The transformer maps dimension names to the keys array positions and merges them with metric values into flat objects. Add CTR formatting (multiply by 100 for percentage display), round average position to one decimal place, and add conditional fields for performance classification (e.g., flagging queries with high impressions but low CTR as 'Opportunity'). Bind the transformed data to a Table component. Configure column types: clicks and impressions as number columns with comma formatting, CTR as percentage, position as number. Sort by impressions descending by default to surface the highest-volume queries first. Add a Chart component showing CTR by top queries using a bar chart. The chart data comes from the top 20 rows of the transformer output, showing which queries have the best and worst CTR for optimization prioritization. Add a date comparison mode by creating a second identical query (getSearchAnalyticsPrev) with dates shifted back one period, then creating a comparison transformer that calculates click and impression changes between the two periods.
1// JavaScript transformer — convert GSC Search Analytics rows to table format2const response = data || {};3const rows = response.rows || [];4const dimensions = ['query']; // match your query's dimensions array56return rows.map(row => {7 const keyMap = {};8 (row.keys || []).forEach((val, idx) => {9 keyMap[dimensions[idx] || `dim_${idx}`] = val;10 });1112 const ctr = row.ctr || 0;13 const position = row.position || 0;1415 return {16 ...keyMap,17 clicks: row.clicks || 0,18 impressions: row.impressions || 0,19 ctr: `${(ctr * 100).toFixed(1)}%`,20 ctr_raw: ctr,21 position: position.toFixed(1),22 position_raw: position,23 // Opportunity flag: high impressions, low CTR, not on page 124 opportunity: row.impressions > 500 && ctr < 0.02 && position > 1025 ? 'High Opportunity'26 : row.impressions > 200 && ctr < 0.0327 ? 'Potential'28 : ''29 };30});Pro tip: Use multiple dimension groupings to get different views of the same data without making separate API calls. Run the query once with dimension=['query'] for the queries table, and cache the results — then use a JavaScript transformer to reaggregate by page or device from the cached data.
Expected result: The Table shows GSC search analytics data with CTR formatted as percentages, position rounded to one decimal, and an 'opportunity' flag highlighting queries with high impressions but low CTR. The Chart shows the top 20 queries by impressions.
Build the complete SEO monitoring dashboard
Assemble all queries into a complete SEO dashboard layout that gives your team a self-service analytics view. Top controls bar: Site URL input (textInput_siteUrl), Date Range Picker (last 28 days default), Dimension Dropdown (Query / Page / Country / Device), URL filter text input for page-specific analysis. A 'Load Data' button triggers getSearchAnalytics and getGSCToken (if token needs refresh). Summary stats row: Total Clicks (sum of clicks column), Total Impressions (sum of impressions), Average CTR (average of ctr_raw values), Average Position (average of position_raw values). Use Retool's Statistic components for these. Main table: Sortable table with all dimension values and metrics. Add conditional row coloring: green for position < 3, yellow for position 4-10, red for position > 20. Add a 'Copy Query' button column that copies the query string to clipboard for use in content tools. Opportunities section: A filtered view of the main table showing only rows where the 'opportunity' flag is set, giving SEO specialists a prioritized list of queries to optimize for. For agencies or teams managing multiple GSC properties, add a property selector dropdown that changes the site URL and re-runs all queries. Store the list of managed properties in a Retool Database table. For complex multi-property SEO dashboards that combine GSC with Google Analytics, Ahrefs, and internal content databases, RapidDev's team can help build the full data architecture.
1// JavaScript query — calculate summary statistics from GSC data2const rows = getSearchAnalytics.data || [];3if (rows.length === 0) return { total_clicks: 0, total_impressions: 0, avg_ctr: '0%', avg_position: 'N/A' };45const totalClicks = rows.reduce((sum, r) => sum + (r.clicks || 0), 0);6const totalImpressions = rows.reduce((sum, r) => sum + (r.impressions || 0), 0);7const avgCtr = totalImpressions > 0 ? totalClicks / totalImpressions : 0;8const avgPos = rows.reduce((sum, r) => sum + (r.position_raw || 0), 0) / rows.length;910return {11 total_clicks: totalClicks.toLocaleString(),12 total_impressions: totalImpressions.toLocaleString(),13 avg_ctr: `${(avgCtr * 100).toFixed(1)}%`,14 avg_position: avgPos.toFixed(1),15 opportunities_count: rows.filter(r => r.opportunity !== '').length16};Pro tip: GSC data has a 2-4 day processing lag. Set your Date Range Picker's maximum date to today minus 3 days to avoid showing incomplete data for the most recent days. Show a note in the UI: 'Data shown through MM/DD — recent days may be incomplete.'
Expected result: The complete SEO dashboard shows summary stats, a sortable query/page table, and an opportunities section. All components respond to the date range and dimension selectors. The dashboard gives SEO teams self-service access to GSC data without needing to open Search Console.
Common use cases
Build a top queries performance dashboard
Create a Retool app that shows the top 100 search queries for any date range with clicks, impressions, CTR, and average position. Add comparison mode to show week-over-week or month-over-month changes. Include a Chart showing CTR trend for selected queries over time.
Build an SEO query dashboard with a date range picker that queries the Search Analytics API for top queries by clicks, shows clicks, impressions, CTR, and position in a sortable Table, and highlights queries where CTR is below 2% but impressions are over 1000 as optimization opportunities.
Copy this prompt to try it in Retool
Page performance monitoring tool
Build a content team dashboard that filters GSC data by page URL prefix (e.g., /blog/) to show how all blog posts are performing in search. Sort by impressions to identify high-visibility pages with low CTR, and by position to find pages ranking on page 2 that are close to breaking into the top 10.
Create a content performance panel where entering a URL prefix filters the Search Analytics results to only pages matching that prefix, then shows all matching pages sorted by impressions descending with their average position highlighted in red if above 10 (below top 10).
Copy this prompt to try it in Retool
Indexing coverage and sitemap monitoring panel
Build an operational dashboard that monitors index coverage status across your site — showing counts of valid, excluded, and error URLs from the URL Inspection API. Alert the team when indexing errors spike or when important pages drop out of the index.
Build an index health panel that queries the Search Console URL Inspection API for key page categories, shows index status (Indexed, Not Indexed, Excluded) for each, and highlights any pages that changed status compared to the previous week's snapshot stored in a Retool Database table.
Copy this prompt to try it in Retool
Troubleshooting
API returns 403 Forbidden — 'The caller does not have permission'
Cause: The service account has not been added as a user in the Search Console property, or was added with Restricted access but is attempting to use an endpoint that requires Full access.
Solution: In Google Search Console → Settings → Users and permissions, verify the service account email appears in the list and has the appropriate access level. Note: adding a new user can take a few minutes to propagate. If you need URL Inspection API access, the account needs 'Full' permission level, not 'Restricted'.
Query returns 'Unknown dimension' or 'Invalid dimensions' error
Cause: The dimensions array in the query body contains an invalid dimension value. GSC only accepts: query, page, country, device, searchAppearance. Any other value causes this error.
Solution: Verify the dimensions array contains only valid values. Check for typos — dimensions are lowercase ('query', not 'Query'). If using a dropdown to select dimensions, ensure the dropdown option values match exactly. Remove empty strings from the dimensions array.
Access token expires after an hour and queries start failing with 401
Cause: Google OAuth access tokens have a 1-hour lifespan. If your Retool app session lasts longer than an hour without a token refresh, the stored gscAccessToken becomes invalid.
Solution: Add a token refresh mechanism: set the getGSCToken query to run on a 50-minute interval using Retool's query settings. Alternatively, check token age before each getSearchAnalytics call and re-trigger getGSCToken if more than 55 minutes have passed. Add error handling on the search analytics query to automatically refresh the token on 401 errors.
1// Event handler: getSearchAnalytics on-failure2// If error is 401, refresh token then retry3if (error.statusCode === 401) {4 await getGSCToken.trigger();5 return getSearchAnalytics.trigger();6}No rows returned even though the site has search traffic
Cause: The siteUrl parameter doesn't match the property URL format in GSC. GSC treats domain properties (sc-domain:example.com) and URL properties (https://www.example.com) as different properties.
Solution: Use the exact site URL format that appears in your Google Search Console property list. For domain-level properties, the format is 'sc-domain:example.com' (no https://). For URL-prefix properties, use the full URL including trailing slash if present. URL-encode this value in the Retool query path.
Best practices
- Use a dedicated service account for Retool's GSC access — add it with the minimum required permission level (Restricted for analytics, Full for URL Inspection) rather than using a personal Google account.
- Store service account credentials as Secret configuration variables — the JSON key file contains a private key that grants read access to all your GSC properties.
- Implement token refresh logic for long-running Retool sessions — Google OAuth tokens expire in 1 hour, so dashboards used throughout the day need automatic token renewal.
- Account for GSC's data lag (2-4 days) by setting date range defaults to exclude the last 3 days and adding a UI note explaining the data freshness.
- Use the URL filter to build page-specific SEO panels for different teams — the content team sees /blog/ performance, the product team sees /features/ pages, the technical team sees the whole site.
- Add performance thresholds to your transformer's opportunity flagging logic based on your site's actual metrics — a 2% CTR threshold is meaningless if your average CTR is 8%.
- Cache GSC data in a Retool Database table using scheduled Workflows — this provides instant load times and enables date comparisons over longer periods than GSC's 16-month data window.
- Combine GSC data with Google Analytics in the same Retool app to show both search visibility (GSC) and on-site behavior (GA) side by side for a complete content performance view.
Alternatives
Google Analytics tracks on-site user behavior and conversion data, while Search Console specifically measures search visibility, query performance, and indexing — both are complementary and work well combined in one Retool app.
Ahrefs provides third-party SEO data including backlinks, competitor rankings, and keyword difficulty that Search Console doesn't have, making it a useful complement for comprehensive SEO dashboards.
SEMrush offers competitive intelligence and broader keyword research data, while Search Console provides authoritative first-party data directly from Google about your own site's search performance.
Frequently asked questions
Do I need a Google Cloud project to use GSC with Retool?
Yes, you need a Google Cloud project to enable the Search Console API and create a service account. The Google Cloud project is free to create and the Search Console API has no usage fees for typical search analytics query volumes. You only need to worry about costs if using other paid Google Cloud services in the same project.
Can I monitor multiple websites from one Retool app?
Yes. Add the service account email as a user on each GSC property you want to monitor. In your Retool app, add a dropdown selector with all your site URLs and make the siteUrl in the API path dynamic based on the dropdown selection. Changing the dropdown selection re-runs the query for the selected property. You can also build a multi-site overview by running queries for each property in parallel and combining the results.
What's the maximum number of rows I can request from the Search Analytics API?
The Search Analytics API returns up to 25,000 rows per request. For sites with more than 25,000 queries or pages, you'll need to implement pagination using the startRow parameter. Most sites are well within this limit. You can request up to 25,000 rows by setting rowLimit to 25000 in your query body.
How do I monitor indexing status and Core Web Vitals through the API?
For indexing coverage, use the Search Console URL Inspection API endpoint: POST /v1/urlInspection/index:inspect with the site URL and inspection URL. This returns the indexing status, discovered date, canonical URL, and crawl information for a specific page. Core Web Vitals data (CrUX data) is available through a separate Google API — the Chrome UX Report API — which has a different authentication setup.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation