# How OpenSEO Integrates Google Search Console Data: A Complete Technical Guide

> Discover how OpenSEO integrates Google Search Console data using a layered architecture for secure client-side-free retrieval. Learn the technical details.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: how-to-guide
- Published: 2026-08-09

---

**OpenSEO connects to Google Search Console through a layered architecture that separates OAuth authentication, business logic in `GscService`, and server-function endpoints, ensuring secure, client-side-free data retrieval.**

The `every-app/open-seo` repository implements a robust Google Search Console (GSC) integration that lets users link their properties, query search analytics, and inspect URLs without exposing credentials to the browser. This guide walks through the exact implementation, from OAuth configuration to performance reporting.

## OAuth Configuration and Constants

All GSC-related constants live in [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts). This file defines the **OAuth provider ID** (`google-search-console`) and required **scopes** for accessing site data and search analytics.

The shared location ensures both frontend and backend use identical values when initiating OAuth flows or validating grants.

## The GSC Client Wrapper

A low-level `createGscClient` function in [`src/server/lib/gscClient.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/gscClient.ts) encapsulates HTTP calls to Google's REST API. This wrapper exposes methods including:

- `listSites` — enumerate accessible properties
- `querySearchAnalytics` — fetch click, impression, position, and CTR data
- `inspectUrl` — run URL inspection for indexing status

The client handles token injection and response parsing, isolating HTTP concerns from business logic.

## GscService: Core Business Logic

The heart of the integration is **`GscService`** in [`src/server/features/gsc/services/GscService.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/gsc/services/GscService.ts). This class coordinates grant detection, connection management, and data operations.

### Grant Detection

`userHasGrant` queries the `account` table for records where `providerId = "google-search-console"`. This determines whether the authenticated user has completed OAuth authorization.

### Connection Management

Three methods handle the project-to-property mapping:

- **`getConnection`** — retrieve the current `GscConnection` for a project
- **`setSite`** — bind a specific GSC property URL to a project
- **`disconnect`** — remove the link via `GscConnectionRepository`

The one-to-one relationship is persisted in `GscConnectionRepository`, storing only the property URL and account reference—never raw Google data.

### Data Retrieval

`getPerformance` forwards structured requests to `querySearchAnalytics`, returning:

- **Rows** — dimension/metric breakdowns (query, page, device, etc.)
- **Request metadata** — total counts, sampling info
- **Property URL** — the connected site's canonical address

### URL Inspection

`inspectUrls` executes batch URL inspections against the connected property. It processes per-URL errors individually while surfacing token failures (expired or revoked grants) for reconnection flows.

### Error Handling Strategy

Two custom error types govern UI behavior:

- **`GscNotConnectedError`** — thrown when a project lacks a linked GSC property
- **`isExpectedGrantFailure`** — detects 401/403 responses and token errors, triggering reconnection prompts instead of generic error states

## Server-Function API Endpoints

The public API surface resides in [`src/serverFunctions/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/gsc.ts). These functions bridge the frontend to `GscService` with clear, typed contracts.

### Grant and Connection Status

```typescript
// Check OAuth grant status
const { connected } = await getGscGrantStatus();
// → { connected: true }

// Get full connection state for a project
const status = await getGscConnection({ projectId: "proj_123" });
// → { connected: true, siteUrl: "https://example.com/", grantValid: true }

```

### Site Selection

`listGscSites` returns all GSC properties the user can access, marking which is currently bound to the project:

```typescript
const { accounts } = await listGscSites({ projectId: "proj_123" });
// accounts: [{ email: "user@gmail.com", sites: [...], current: true }]

```

### Connection Lifecycle

```typescript
// Bind a property to the project
await setGscSite({
  projectId: "proj_123",
  accountId: "gsc_acc_456",
  siteUrl: "https://example.com/",
});

// Remove the link
await disconnectGsc({ projectId: "proj_123" });

```

### Self-Hosted OAuth

`startSelfHostedGscLink` generates an authorization URL for deployments without managed OAuth credentials, enabling custom client ID/secret configurations.

## Performance Reporting Integration

Higher-level analytics consume GSC data through [`src/serverFunctions/searchPerformance.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/searchPerformance.ts). These endpoints call `GscService.getPerformance` to assemble:

- Dashboard visualizations
- Filtered data tables
- CSV exports

Example report request:

```typescript
const report = await getSearchPerformanceReport({
  projectId: "proj_123",
  dateRange: { start: "2024-01-01", end: "2024-01-31" },
  device: "mobile",
  dimensions: ["query", "page"],
});
console.log(report.totals); // { clicks: 15234, impressions: 89012, ctr: 0.171, position: 12.4 }

```

## MCP Debugging Tools

The integration also powers debugging utilities in [`src/server/mcp/tools/search-console-tools.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/search-console-tools.ts). These Model Context Protocol tools let developers programmatically query GSC data during troubleshooting sessions.

## Complete Data Flow

```

Frontend          Server Function          GscService          GSC Client          Google API
   │                    │                      │                    │                  │
   │ getGscGrantStatus  │                      │                    │                  │
   │───────────────────>│                      │                    │                  │
   │                    │ userHasGrant         │                    │                  │
   │                    │─────────────────────>│ check account table│                  │
   │                    │<─────────────────────│                    │                  │
   │ { connected: true }│                      │                    │                  │
   │<───────────────────│                      │                    │                  │
   │                    │                      │                    │                  │
   │ setGscSite         │                      │                    │                  │
   │───────────────────>│ setSite              │                    │                  │
   │                    │─────────────────────>│ persist via        │                  │
   │                    │                      │ GscConnectionRepo  │                  │
   │                    │<─────────────────────│                    │                  │
   │ success            │                      │                    │                  │
   │<───────────────────│                      │                    │                  │
   │                    │                      │                    │                  │
   │ getSearchPerformanceReport                  │                    │                  │
   │───────────────────>│                      │                    │                  │
   │                    │ getPerformance       │                    │                  │
   │                    │─────────────────────>│ querySearchAnalytics                  │
   │                    │                      │───────────────────>│                  │
   │                    │                      │                    │─── REST call ───>│
   │                    │                      │                    │<─── response─────│
   │                    │                      │<───────────────────│                  │
   │                    │<─────────────────────│ rows + metadata    │                  │
   │ report object      │                      │                    │                  │
   │<───────────────────│                      │                    │                  │

```

All GSC operations execute **server-side only**, using the user's stored OAuth token. No Google credentials or analytical data reach the browser.

## Summary

- **OAuth constants** in [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts) define provider IDs and scopes
- **GSC client** ([`src/server/lib/gscClient.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/gscClient.ts)) wraps Google's REST API
- **GscService** ([`src/server/features/gsc/services/GscService.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/gsc/services/GscService.ts)) implements grant detection, connection management, data retrieval, and error handling
- **Server functions** ([`src/serverFunctions/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/gsc.ts)) expose typed endpoints for grant status, site listing, connection lifecycle, and self-hosted OAuth
- **Performance reports** ([`src/serverFunctions/searchPerformance.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/searchPerformance.ts)) consume GSC data for dashboards and exports
- **Security model** keeps tokens server-side, persists only connection metadata, and surfaces grant failures for clean reconnection UX

## Frequently Asked Questions

### What OAuth scopes does OpenSEO request for Google Search Console?

According to [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts), OpenSEO requests scopes for site verification and search analytics read access. The exact scope strings are centralized in this shared module so both authorization URL generation and validation remain consistent across the application.

### How does OpenSEO handle expired or revoked GSC grants?

`GscService.isExpectedGrantFailure` identifies 401/403 responses and token-related errors from Google. Instead of throwing internal server errors, these trigger the reconnection UI flow, prompting users to reauthorize while preserving their project settings.

### Can OpenSEO connect multiple GSC properties to one project?

No—the architecture enforces a one-to-one mapping. `GscService.setSite` replaces any existing connection for a project, and `GscConnectionRepository` stores a single property URL per project. Users must disconnect before switching properties.

### Where is raw Google Search Console data stored?

Only connection metadata (property URL, account reference) persists in OpenSEO's database. Actual search analytics data is fetched on-demand through `GscService.getPerformance` and never cached or stored permanently, ensuring compliance with Google's data policies.