How to Integrate Google Search Console (GSC) with OpenSEO: Complete Setup Guide

OpenSEO supports a first-party Google Search Console integration that pulls click, impression, and position data directly from your Google account using OAuth 2.0 and the webmasters.readonly scope, working in both hosted and self-hosted deployments.

OpenSEO is an open-source SEO platform that includes native support for Google Search Console. The integration allows you to analyze search performance without manually exporting CSV files, using a secure OAuth flow that encrypts tokens at rest. Whether you run the every-app/open-seo repository in the cloud or on your own infrastructure, the architecture supports seamless GSC data retrieval through a type-safe API layer.

Prerequisites and Environment Configuration

Before initiating the OAuth flow, self-hosted instances must configure three critical environment variables to authenticate with Google’s API.

Required Environment Variables

The integration requires the following variables in your deployment environment:

  • GOOGLE_CLIENT_ID – Your OAuth 2.0 Client ID from Google Cloud Console
  • GOOGLE_CLIENT_SECRET – The corresponding Client Secret
  • BETTER_AUTH_SECRET – A sufficiently long encryption key used by Better-Auth to encrypt OAuth tokens at rest

In src/server/features/gsc/oauth-config.ts, the helper function hasSelfHostedGscConfig validates these variables before surfacing the "Connect with Google" UI. If any variable is missing, the integration remains disabled to prevent runtime errors.

Google Cloud Project Setup

You must create a Google Cloud project and enable the Google Search Console API. Configure the OAuth consent screen as an External app and add authorized test users if your app remains in testing mode. Create OAuth 2.0 credentials (Web application type) and set the authorized redirect URI to https://<your-domain>/api/gsc/oauth/callback.

OAuth Provider Architecture

OpenSEO uses Better-Auth to manage the OAuth handshake, with a dedicated provider configuration for Google Search Console.

Better-Auth Integration

The OAuth provider is defined in src/shared/gsc.ts and consumed by the generic auth plugin in src/lib/auth-config.ts. The provider requests the webmasters.readonly scope, which grants read-only access to GSC data without permission to modify site settings.

// Conceptual flow from src/shared/gsc.ts and src/lib/auth-config.ts
const gscProvider = {
  id: "google-search-console",
  scopes: ["webmasters.readonly"],
  // ... additional OAuth configuration
};

Permission Scopes and Security

The webmasters.readonly scope limits OpenSEO to viewing search analytics, site listings, and URL inspection data. It cannot submit URL removals or change site configurations. All OAuth tokens are encrypted using BETTER_AUTH_SECRET before storage in the database, ensuring credentials remain secure even if database backups are compromised.

Connecting Your GSC Account

The connection process involves two distinct phases: initial authorization and property selection.

The Connection Flow

When users click Integrations → GSC Insights → Connect with Google, the client invokes startSelfHostedGscLink (exposed in src/serverFunctions/gsc.ts). This function:

  1. Validates the self-hosted configuration via hasSelfHostedGscConfig
  2. Constructs the authorization URL with the correct state parameter
  3. Redirects the user to Google’s OAuth consent screen

After authorization, Google redirects to /api/gsc/oauth/callback, where the system exchanges the code for tokens and stores them via Better-Auth’s encrypted credential store.

Selecting a Property

Once authenticated, the UI calls listGscSites (from src/serverFunctions/gsc.ts) to retrieve all verified properties. The endpoint returns an array of accounts with their associated sites, marking properties that require reconnection due to expired grants.

// Example response from listGscSites
{
  accounts: [{
    accountId: "acct_456",
    email: "admin@example.com",
    requiresReconnect: false,
    sites: [{
      siteUrl: "https://example.com",
      isSelected: false,
      selectable: true
    }]
  }]
}

When a user selects a site, the client sends a POST request to setGscSite, which invokes GscService.setSite in src/server/features/gsc/services/GscService.ts. This persists the mapping in the gsc_connection table and associates the OAuth account with the specific OpenSEO project.

Retrieving Search Console Data

After connection, OpenSEO provides several server functions to query GSC data programmatically.

Service Layer Architecture

GscService (src/server/features/gsc/services/GscService.ts) orchestrates all GSC operations. It wraps the low-level HTTP client defined in src/server/lib/gscClient.ts, which handles requests to /sites and /searchAnalytics/query endpoints. The service normalizes API errors into GscApiError and GscTokenError classes, allowing the UI to distinguish between permission issues and transient API failures.

Available API Endpoints

The public API exposed through src/serverFunctions/gsc.ts includes:

  • getGscGrantStatus – Checks if the current user has an active OAuth grant
  • getGscConnection – Returns the selected property URL and connection state for a project
  • listGscSites – Lists all accessible GSC properties across all connected Google accounts
  • setGscSite – Binds a chosen property to a specific project
  • disconnectGsc – Removes the project binding and revokes the OAuth account if unused by other projects

Database Schema and Security

OpenSEO maintains a dedicated schema for GSC connections with encrypted credential storage.

The gsc_connection Table

Defined in src/db/gsc.schema.ts, the gsc_connection table stores:

  • project_id – Foreign key to the OpenSEO project
  • site_url – The selected GSC property URL
  • account_id – Reference to the encrypted OAuth credential stored by Better-Auth
  • created_at/updated_at – Timestamps for audit trails

Token Encryption

Better-Auth encrypts OAuth access tokens and refresh tokens using the BETTER_AUTH_SECRET environment variable before writing them to the database. This means raw Google credentials never exist in plain text in the database or application logs.

Step-by-Step Self-Hosted Setup

Follow these steps to integrate Google Search Console in a self-hosted OpenSEO instance:

  1. Create a Google Cloud project and enable the Google Search Console API
  2. Configure OAuth consent as an External app and add test users
  3. Create an OAuth client ID (Web application type) and set the redirect URI to https://<your-domain>/api/gsc/oauth/callback
  4. Add environment variables (GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, BETTER_AUTH_SECRET) to your .env file or deployment dashboard
  5. Restart OpenSEO to load the new configuration
  6. Navigate to Integrations → GSC Insights and click "Connect with Google"
  7. Select a verified property from the site picker populated by listGscSites
  8. Verify connection by checking that the GSC Insights dashboard populates with click and impression data

Programmatic API Usage

You can interact with the GSC integration programmatically using the server functions. These examples assume an authenticated session with appropriate cookies.

Check Grant Status

const status = await fetch('/api/gsc/grant-status', { method: 'GET' })
  .then(r => r.json());
// Returns: { connected: true } or { connected: false }

Retrieve Connection Details

const connection = await fetch('/api/gsc/connection', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ projectId: 'proj_123' })
}).then(r => r.json());
// Returns: { connected: true, siteUrl: 'https://example.com', ... }

List Available Sites

const sites = await fetch('/api/gsc/sites', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ projectId: 'proj_123' })
}).then(r => r.json());
// Returns: { accounts: [{ accountId, email, requiresReconnect, sites: [...] }] }

Bind a Site to Project

const result = await fetch('/api/gsc/set-site', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    projectId: 'proj_123',
    accountId: 'acct_456',
    siteUrl: 'https://example.com'
  })
}).then(r => r.json());
// Returns: { connected: true, siteUrl: 'https://example.com' }

Disconnect GSC

const result = await fetch('/api/gsc/disconnect', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ projectId: 'proj_123' })
}).then(r => r.json());
// Returns: { connected: false }

Summary

  • OpenSEO integrates Google Search Console through a first-party OAuth 2.0 flow using the webmasters.readonly scope
  • Configuration requires three environment variables: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and BETTER_AUTH_SECRET
  • Architecture includes a Better-Auth provider in src/shared/gsc.ts, a service layer in src/server/features/gsc/services/GscService.ts, and API endpoints in src/serverFunctions/gsc.ts
  • Security relies on Better-Auth’s encryption for OAuth tokens and the gsc_connection table in src/db/gsc.schema.ts for project mappings
  • Self-hosted deployments must configure Google Cloud OAuth credentials with the redirect URI pointing to /api/gsc/oauth/callback

Frequently Asked Questions

Do I need to self-host OpenSEO to use Google Search Console integration?

No. OpenSEO supports GSC integration in both hosted and self-hosted deployments. However, self-hosted instances require additional configuration of Google Cloud OAuth credentials and environment variables, while the hosted version handles OAuth provider setup automatically.

What permissions does OpenSEO request from my Google account?

OpenSEO requests the webmasters.readonly OAuth scope, which grants read-only access to your Search Console data. This permission allows OpenSEO to retrieve click counts, impressions, and position data, but cannot modify site settings, remove URLs, or submit sitemaps.

Where are my Google OAuth tokens stored?

OAuth tokens are encrypted at rest using your BETTER_AUTH_SECRET environment variable and stored in the database managed by Better-Auth. The gsc_connection table in src/db/gsc.schema.ts only stores references to these encrypted credentials, not the tokens themselves.

Can I connect multiple Google Search Console properties to one OpenSEO project?

No, each OpenSEO project binds to a single GSC property URL at a time. However, you can disconnect and reconnect to different properties using the setGscSite endpoint, or create separate OpenSEO projects for different GSC properties. The listGscSites endpoint shows all properties available across all Google accounts connected to your user.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →