Back to Blog

How to Use the Google Search Console API: A Developer's Guide

·, Founder·Updated ·Technical SEO
google search consolegsc apiseo automationgoogle seo apisearch console to json

Google Search Console (GSC) is the single best source of truth for how your site performs in Google search. The web UI is fine for a quick glance, but if you want to build dashboards, automate reporting, or feed SEO data into your AI workflows, you need the API.

This guide walks you through setting up authentication, making your first query, and building something useful — all with working code.

Prerequisites

  • A Google Cloud project with the Search Console API enabled
  • A verified site in Google Search Console
  • Node.js 18+ (examples use TypeScript, but the API is language-agnostic)

Step 1: Authentication

The GSC API uses OAuth 2.0. You have two options:

Option A: Service Account (server-to-server)

Best for automated workflows and CI pipelines.

  1. Create a service account in Google Cloud Console
  2. Download the JSON key file
  3. Add the service account email as a user in GSC (Settings → Users and permissions → Add user)
import { google } from "googleapis";

const auth = new google.auth.GoogleAuth({
  keyFile: "./service-account.json",
  scopes: ["https://www.googleapis.com/auth/webmasters.readonly"],
});

const searchconsole = google.searchconsole({ version: "v1", auth });

Option B: OAuth 2.0 (user consent)

Best for apps where users connect their own GSC properties.

const oauth2Client = new google.auth.OAuth2(
  process.env.GOOGLE_CLIENT_ID,
  process.env.GOOGLE_CLIENT_SECRET,
  "http://localhost:3000/api/auth/callback"
);

// Generate consent URL
const authUrl = oauth2Client.generateAuthUrl({
  access_type: "offline",
  scope: ["https://www.googleapis.com/auth/webmasters.readonly"],
});

// After user consents, exchange code for tokens
const { tokens } = await oauth2Client.getToken(code);
oauth2Client.setCredentials(tokens);

Step 2: List Verified Sites

Before querying analytics, verify which sites the account has access to:

const sites = await searchconsole.sites.list();

for (const site of sites.data.siteEntry || []) {
  console.log(`${site.siteUrl} — ${site.permissionLevel}`);
}

Output:

https://example.com/ — siteOwner
https://blog.example.com/ — siteFullUser

Step 3: Query Search Analytics

This is the core endpoint. It returns queries, pages, clicks, impressions, CTR, and position data.

const response = await searchconsole.searchanalytics.query({
  siteUrl: "https://example.com",
  requestBody: {
    startDate: "2026-01-01",
    endDate: "2026-03-20",
    dimensions: ["query", "page"],
    rowLimit: 25,
    dataState: "final",
  },
});

for (const row of response.data.rows || []) {
  const [query, page] = row.keys!;
  console.log(
    `"${query}" → ${page} | pos ${row.position?.toFixed(1)} | ${row.clicks} clicks`
  );
}

Dimensions You Can Use

DimensionDescription
queryThe search term the user typed
pageThe URL that appeared in results
deviceDESKTOP, MOBILE, or TABLET
countryISO 3166-1 alpha-3 country code
dateIndividual date (for time series)
searchAppearanceSearch appearance type, such as rich result features. Query this dimension on its own to list the values your site has

Filtering Results

Narrow your data with dimension filters:

const response = await searchconsole.searchanalytics.query({
  siteUrl: "https://example.com",
  requestBody: {
    startDate: "2026-01-01",
    endDate: "2026-03-20",
    dimensions: ["query"],
    dimensionFilterGroups: [
      {
        filters: [
          {
            dimension: "page",
            operator: "contains",
            expression: "/blog/",
          },
          {
            dimension: "query",
            operator: "notContains",
            expression: "brand-name",
          },
        ],
      },
    ],
    rowLimit: 100,
  },
});

Step 4: Build Something Useful

Automated Position Tracking

Track how your rankings change over time by storing daily snapshots:

async function trackPositions(siteUrl: string, queries: string[]) {
  const today = new Date().toISOString().split("T")[0];
  const sevenDaysAgo = new Date(Date.now() - 7 * 86400000)
    .toISOString()
    .split("T")[0];

  const response = await searchconsole.searchanalytics.query({
    siteUrl,
    requestBody: {
      startDate: sevenDaysAgo,
      endDate: today,
      dimensions: ["query", "date"],
      dimensionFilterGroups: [
        {
          // Filters in a group are combined with AND, so match every
          // tracked query with one regex instead of several "equals" filters
          filters: [
            {
              dimension: "query",
              operator: "includingRegex",
              expression: `^(${queries
                .map((q) => q.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
                .join("|")})$`,
            },
          ],
        },
      ],
    },
  });

  // Group by query and detect position changes
  const byQuery = new Map<string, { date: string; position: number }[]>();

  for (const row of response.data.rows || []) {
    const [query, date] = row.keys!;
    if (!byQuery.has(query)) byQuery.set(query, []);
    byQuery.get(query)!.push({ date, position: row.position! });
  }

  for (const [query, data] of byQuery) {
    const sorted = data.sort((a, b) => a.date.localeCompare(b.date));
    const first = sorted[0].position;
    const last = sorted[sorted.length - 1].position;
    const delta = first - last; // positive = improved
    console.log(
      `"${query}": ${last.toFixed(1)} (${delta > 0 ? "↑" : "↓"} ${Math.abs(delta).toFixed(1)})`
    );
  }
}

Content Gap Detection

Find queries where you rank between positions 8 and 20, at the bottom of page 1 or on page 2. These are often your best opportunities:

async function findContentGaps(siteUrl: string) {
  const response = await searchconsole.searchanalytics.query({
    siteUrl,
    requestBody: {
      startDate: "2026-01-01",
      endDate: "2026-03-20",
      dimensions: ["query", "page"],
      rowLimit: 1000,
    },
  });

  const gaps = (response.data.rows || [])
    .filter((row) => row.position! >= 8 && row.position! <= 20)
    .filter((row) => row.impressions! > 100)
    .sort((a, b) => b.impressions! - a.impressions!);

  console.log("Content gaps (high impressions, page 2):");
  for (const row of gaps.slice(0, 20)) {
    const [query, page] = row.keys!;
    console.log(
      `  "${query}" → pos ${row.position?.toFixed(1)} | ${row.impressions} imp | ${page}`
    );
  }
}

Google Search Console to JSON

If you need GSC data as JSON for dashboards, CI pipelines, or AI workflows, you have two paths: the raw API (shown above) or the XeoRank CLI, which reads the GSC data you have connected to XeoRank and prints it as JSON.

Using the XeoRank CLI

# Get your top queries as JSON
$ xyle queries --site example.com --limit 50 --json

# Full Search Console overview as JSON
$ xyle search-console overview --site example.com --json

# Export to a file for further processing
$ xyle queries --site example.com --json > gsc-queries.json

The JSON output includes clicks, impressions, CTR, and average position for each query — ready to pipe into jq, load into a database, or feed into an AI agent's context.

Converting API Responses to JSON

If you are using the raw API, the response is already JSON. Here is a utility to flatten the nested structure into a clean format:

interface GSCRow {
  query: string;
  page: string;
  clicks: number;
  impressions: number;
  ctr: number;
  position: number;
}

function flattenGSCResponse(response: any): GSCRow[] {
  return (response.data.rows || []).map((row: any) => ({
    query: row.keys?.[0] || "",
    page: row.keys?.[1] || "",
    clicks: row.clicks || 0,
    impressions: row.impressions || 0,
    ctr: row.ctr || 0,
    position: row.position || 0,
  }));
}

// Usage
const data = flattenGSCResponse(response);
console.log(JSON.stringify(data, null, 2));

Using the GSC API with Python

The GSC API works with any language. Here is the Python equivalent using the official Google client library.

Setup

pip install google-api-python-client google-auth

Authentication with a Service Account

from google.oauth2 import service_account
from googleapiclient.discovery import build

credentials = service_account.Credentials.from_service_account_file(
    "service-account.json",
    scopes=["https://www.googleapis.com/auth/webmasters.readonly"],
)

service = build("searchconsole", "v1", credentials=credentials)

Querying Search Analytics

response = service.searchanalytics().query(
    siteUrl="https://example.com",
    body={
        "startDate": "2026-01-01",
        "endDate": "2026-04-15",
        "dimensions": ["query", "page"],
        "rowLimit": 100,
        "dataState": "final",
    },
).execute()

for row in response.get("rows", []):
    query, page = row["keys"]
    print(f'"{query}" → {page} | pos {row["position"]:.1f} | {row["clicks"]} clicks')

Exporting to JSON

import json

rows = [
    {
        "query": row["keys"][0],
        "page": row["keys"][1],
        "clicks": row["clicks"],
        "impressions": row["impressions"],
        "ctr": row["ctr"],
        "position": row["position"],
    }
    for row in response.get("rows", [])
]

with open("gsc-data.json", "w") as f:
    json.dump(rows, f, indent=2)

Filtering by Page or Query

response = service.searchanalytics().query(
    siteUrl="https://example.com",
    body={
        "startDate": "2026-01-01",
        "endDate": "2026-04-15",
        "dimensions": ["query"],
        "dimensionFilterGroups": [
            {
                "filters": [
                    {
                        "dimension": "page",
                        "operator": "contains",
                        "expression": "/blog/",
                    }
                ]
            }
        ],
        "rowLimit": 100,
    },
).execute()

Google SEO API Alternatives

The GSC API is the primary source of Google search data, but it has limitations: 16 months of history, 25,000 rows per request, and finalized data that lags by a few days. Here are alternatives and complementary APIs.

APIData ProvidedBest ForLimitation
GSC APIQueries, clicks, impressions, positionCore search performance16-month history, final data lags a few days
Google PageSpeed APICore Web Vitals, performance scoresPage speed monitoringPer-URL, no bulk queries
Google Indexing APISubmit URLs for indexingFast indexing of new contentOnly for JobPosting and BroadcastEvent officially
XeoRank APISEO + AEO + GEO scores, GSC syncCombined SEO/AI analysisRequires XeoRank account

For one-off checks you do not need an API at all. The free sitemap checker and page analysis work on any public URL.

The XeoRank API

If you want GSC data combined with SEO analysis, AEO signals, and GEO scores in one place, XeoRank provides this. Connect your GSC properties once, sync them, and query the results through the API or CLI:

# XeoRank CLI: GSC data plus SEO/AEO/GEO analysis
$ xyle queries --site example.com --json                     # GSC query data
$ xyle crawl --url https://example.com --json                # Full page analysis
$ xyle analyze --url https://example.com --text "..." --json # Scores and recommendations
$ xyle gaps --page https://example.com/blog/post             # Content gaps for a page

The advantage over the raw GSC API: you skip writing your own auth and pagination code, and the same tool can crawl and score the pages behind each query.

Rate Limits & Best Practices

The GSC API has the following limits (see Google's usage limits page for the current numbers):

  • Search Analytics queries per minute: 1,200 per site and 1,200 per user, with higher per-project limits
  • Row limit per request: 25,000 max
  • Date range: Up to 16 months of data
  • Data freshness: Finalized data lags by a few days; dataState: "all" includes fresher data that may still change

Tips:

  • Cache responses aggressively — GSC data does not change retroactively for finalized dates.
  • Use dataState: "final" for accurate data (it leaves out recent days that are not final yet).
  • Paginate with startRow for large result sets.
  • Store historical data in your own database — GSC only retains 16 months.

Skip the Boilerplate with XeoRank

If you would rather skip the auth setup, pagination logic, and data storage, XeoRank handles them. Connect your GSC properties once, then access your data via the dashboard, CLI, or REST API.

$ npm install -g @xyleapp/cli
$ xyle login
$ xyle queries --site example.com --limit 20
$ xyle gaps --page https://example.com/blog/post

XeoRank stores your GSC data each time you sync, finds content gaps, detects AEO and GEO signals (schema markup, content structure, quality metrics), and gives you SEO, AEO and GEO scores. Use the dashboard analysis page for visual results or the CLI for scripting.

Whether you build your own integration or use a tool like XeoRank, the key takeaway is this: your GSC data is too valuable to only view in a web UI. Programmatic access opens up automated monitoring, AI-powered optimization for both search engines and AI answer engines, and workflows that scale with your site.

Frequently Asked Questions

How do I convert Google Search Console data to JSON?

Use the GSC API directly — it returns JSON by default. For a simpler approach, the XeoRank CLI provides xyle queries --json and xyle search-console overview --json which output clean, pre-formatted JSON without needing to handle authentication or pagination yourself.

Is there a Python library for the Google Search Console API?

Yes. Use Google's official google-api-python-client library with google-auth for authentication. Install with pip install google-api-python-client google-auth, authenticate with a service account, and call service.searchanalytics().query() to retrieve data. See the Python section above for complete examples.

What is the difference between the Google SEO API and the Search Console API?

There is no separate "Google SEO API." The Google Search Console API is the primary API for accessing search performance data (queries, clicks, impressions, positions). Other Google APIs like the PageSpeed Insights API and Indexing API provide complementary data, but the GSC API is the core tool for SEO analytics.

Ready to optimize your search rankings?

XeoRank connects to Google Search Console, analyzes content gaps with AI, and gives you actionable fixes — from the terminal or dashboard.