You're tracking keyword rankings manually in Google Search Console. Every morning: log in, filter by query, select date range, export to CSV, paste into a spreadsheet. Repeat for each keyword. By Friday, you've spent hours collecting data that should take minutes.
This n8n workflow connects directly to the Google Search Console API, fetches position and impression data for configured keywords, and updates a Google Sheets tracking spreadsheet automatically. Here's how to architect this integration in n8n.
Architecture Overview
The workflow follows this data flow:
1. Read keyword/country config from Google Sheets
2. Read date range from Google Sheets
3. Loop through dates (one at a time)
4. For each date:
- Query Search Console API with keyword filters
- Check if position data exists
- Update tracking sheet with position + impressions
5. Move to next date
Why this architecture? Processing one date at a time prevents API rate limit issues. Google Search Console has quotas, and batch processing can trigger throttling. The IF node handles missing data gracefully—if a keyword has no impressions for a date, the workflow skips that update without crashing.
Alternative considered: Querying all dates in a single API call. Rejected because: (1) harder to handle partial failures, (2) less granular error tracking, (3) complicates the update logic when matching dates to spreadsheet rows.
Google Search Console API Integration
Authentication: OAuth2 Setup
The Search Console API requires OAuth2 credentials. You'll need:
- A project in Google Cloud Console
- Search Console API enabled
- OAuth2 credentials with scope:
https://www.googleapis.com/auth/webmasters.readonly - Credentials configured in n8n (Settings → Credentials → Google Search Console OAuth2 API)
API Request Structure
The core API call is an HTTP POST to the searchAnalytics endpoint:
POST https://www.googleapis.com/webmasters/v3/sites/https%3A%2F%2Fexample.com%2F/searchAnalytics/query
Headers:
Authorization: Bearer {oauth_token}
Content-Type: application/json
Body:
{
"startDate": "2025-02-06",
"endDate": "2025-02-06",
"searchType": "web",
"dimensions": ["query"],
"dimensionFilterGroups": [{
"filters": [
{"dimension": "query", "expression": "exact keyword", "operator": "equals"},
{"dimension": "country", "expression": "usa", "operator": "equals"}
]
}]
}
Critical parameter: The site URL in the endpoint must be URL-encoded. For https://example.com/, use https%3A%2F%2Fexample.com%2F. Find your exact property URL in Search Console settings.
API Response Structure
When position data exists:
{
"rows": [
{
"keys": ["your keyword"],
"position": 12.5,
"impressions": 847,
"clicks": 23,
"ctr": 0.027
}
]
}
When no data exists (keyword had no impressions):
{}
Handling missing data: The workflow uses an IF node to check $json.rows[0].position. If this field doesn't exist, it skips the Google Sheets update and continues to the next date. This prevents errors when tracking new keywords or low-impression queries.
n8n Configuration Deep-Dive
Google Sheets Integration
Configuration sheet structure:
- Sheet 1 ("keyword & country"): columns
keyword,country - Sheet 2 ("dates"): column
datein YYYY-MM-DD format - Sheet 3 ("position"): columns
date,position,impressions
Reading configuration:
// Node: Google Sheets (Get Rows)
Resource: Sheet Within Document
Operation: Get Row(s)
Document: [Your spreadsheet]
Sheet: "keyword & country"
// Returns array of {keyword: "...", country: "..."}
Updating position data:
// Node: Google Sheets (Update Row)
Operation: Update Row
Column to match on: "date"
Values to update:
- date: {{ $('Loop Through Dates').item.json.date }}
- position: {{ $json.rows[0].position }}
- impressions: {{ $json.rows[0].impressions }}
Why Update Row instead of Append? Update Row allows re-running the workflow for the same dates without creating duplicates. Pre-populate your position sheet with dates, and the workflow will fill in position/impression data as it runs.
Split In Batches Configuration
// Node: Split In Batches
Batch Size: 1
// Processes one date per iteration
// Loops back to HTTP Request node until all dates processed
Rate limiting strategy: With batch size 1, you're making ~1 API request per second. Search Console's quota is generous (thousands of queries per day), but processing one at a time keeps you safely under limits. For large backlogs (90+ days), add a Wait node with 1-second delay after each API call.
Implementation Gotchas
Search Console data delay: Search Console finalizes data 2-3 days after the date occurs. If you query yesterday's data, you'll get incomplete results. Always track dates that are at least 3 days old.
Credential scope errors: If you see "Insufficient authentication scopes", your OAuth credential is missing the webmasters scope. Recreate the credential in n8n with the correct scope, then re-authenticate.
Empty responses vs. API errors: The workflow differentiates between "no data for this keyword" (empty response, status 200) and "API error" (status 400/500). The IF node only handles empty responses. API errors will fail the workflow—check your property URL encoding and credential scopes.
Date format mismatches: The Search Console API requires YYYY-MM-DD format. Google Sheets might auto-format dates as MM/DD/YYYY. Use a Set node to transform dates before passing them to the API:
// Node: Set (Format Date)
Value: {{ $json.date_raw.split('/').reverse().join('-') }}
// Converts 02/09/2025 to 2025-02-09
Missing position field vs. null position: The API returns no rows array when a keyword has zero impressions. It returns rows[0].position: null when position data exists but is unavailable (rare). The IF node condition does not exist catches both cases.
Prerequisites
Required accounts:
- n8n instance (self-hosted or n8n Cloud)
- Google Search Console access with verified property
- Google Sheets account
- Google Cloud project with Search Console API enabled
API credentials:
- OAuth2 client ID and secret from Google Cloud Console
- Credential configured in n8n: Settings → Credentials → Google Search Console OAuth2 API
- Scope:
https://www.googleapis.com/auth/webmasters.readonly
Google Sheets template:
- Sheet "keyword & country" with columns: keyword, country
- Sheet "dates" with column: date (YYYY-MM-DD format)
- Sheet "position" with columns: date, position, impressions
Pre-populate dates sheet with your tracking period (e.g., last 30 days). Pre-populate position sheet with matching dates (empty position/impressions columns).
Estimated API costs: Google Search Console API is free with generous quotas (thousands of queries per day). No usage fees.
Get the Complete n8n Workflow Configuration
This tutorial covers the API integration architecture and core concepts. For the complete n8n workflow JSON with all node configurations, Google Sheets template, and a video walkthrough of the setup process, check out the full implementation guide.
Top comments (0)