# How to Integrate Google Search Console Data into OpenSEO: A Complete Technical Guide

> Learn how to integrate Google Search Console data into OpenSEO. This technical guide explains the layered architecture for secure, client-side-free data retrieval using OAuth.

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

---

**OpenSEO integrates Google Search Console data through a layered architecture that separates OAuth authentication, service-level business logic, and server-function API endpoints, ensuring secure, client-side-free data retrieval using the user's own OAuth tokens.**

The `every-app/open-seo` repository implements a robust **Google Search Console (GSC) integration** that allows projects to pull search analytics, inspect URLs, and manage property connections without persisting raw Google data beyond the essential connection records. This guide examines the exact file structure, service methods, and implementation patterns used to stream GSC data into the platform's reporting UI.

## OAuth Configuration and Constants

All Google Search Console OAuth configuration resides in [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts). This file defines the **provider ID** (`google-search-console`) and required **OAuth scopes** that enable the application to query search analytics and site data on behalf of the user.

The low-level HTTP client wrapper, `createGscClient`, is exported from [`src/server/lib/gscClient.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/gscClient.ts) (imported throughout the service layer). This client encapsulates direct calls to the Google Search Console REST API, including `listSites`, `querySearchAnalytics`, and `inspectUrl` operations.

## The GscService Layer

The core business logic for **integrating Google Search Console data into OpenSEO** lives 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 service orchestrates grant detection, connection management, and data retrieval while handling token-related failures gracefully.

### Grant Detection

The `userHasGrant` method checks the `account` table for an existing Google Search Console grant by searching for `providerId = "google-search-console"`. This determines whether the authenticated user has previously authorized the application to access their GSC data.

### Connection Handling

**`getConnection`**, **`setSite`**, and **`disconnect`** manage the one-to-one mapping between an OpenSEO project and a GSC property:

- **`getConnection`**: Retrieves the current project-to-property link from `GscConnectionRepository`
- **`setSite`**: Binds a selected GSC property to a specific project, persisting the relationship
- **`disconnect`**: Removes the link between a project and its associated GSC property

### Data Retrieval

The **`getPerformance`** method forwards `searchAnalytics.query` requests to the GSC client. It returns rows of search performance data, request metadata, and the property's URL, which powers the platform's dashboard and CSV export features.

### URL Inspection

**`inspectUrls`** runs batch URL inspections against the connected property. This method handles per-URL errors individually while propagating authentication failures (token expiration or revocation) upward for UI handling.

### Error Handling

The service defines **`GscNotConnectedError`** to signal when a project lacks a linked GSC property. The **`isExpectedGrantFailure`** utility identifies 401/403 responses or token-related errors that should trigger a reconnection UI rather than display as internal server errors.

## Server-Function API Endpoints

The public API exposed to the frontend resides in [`src/serverFunctions/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/gsc.ts). These server functions bridge the UI and the `GscService` layer:

- **`getGscGrantStatus`**: Returns whether the authenticated user has authorized GSC access (`{ connected: true|false }`)
- **`getGscConnection`**: Provides the current project's connection status, including grant validity and site URL
- **`listGscSites`**: Lists all GSC properties available to the user, marking which site is currently bound to the project
- **`setGscSite`**: Executes `GscService.setSite` to bind a selected property
- **`disconnectGsc`**: Removes the project-to-property link via `GscService.disconnect`
- **`startSelfHostedGscLink`**: Generates an OAuth authorization URL for self-hosted deployments

## Performance Data Integration

Search performance reports leverage [`src/serverFunctions/searchPerformance.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/searchPerformance.ts), which calls `GscService.getPerformance` to assemble dashboard data, analytics tables, and CSV exports. Additionally, the MCP debugging tools 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) utilize the same service methods for development and diagnostics.

The data flow follows this architecture:

```

Frontend → Server Functions (gsc.ts) → GscService → GscConnectionRepository / GSC Client → Google Search Console API

```

All GSC data fetching occurs **server-side only**, using the user's own OAuth token without exposing credentials to the client or persisting raw Google data beyond the project-specific connection record.

## Implementation Examples

Check grant status before displaying GSC features:

```typescript
const { connected } = await getGscGrantStatus();
// Returns: { connected: true } or { connected: false }

```

List available properties for the user to select:

```typescript
const { accounts } = await listGscSites({ projectId: "proj_123" });

accounts.forEach(account => {
  console.log(`Account ${account.email} has ${account.sites.length} sites`);
});

```

Connect a project to a specific GSC property:

```typescript
await setGscSite({
  projectId: "proj_123",
  accountId: "gsc_acc_456",
  siteUrl: "https://example.com/",
});

```

Fetch search performance metrics for a date range:

```typescript
const report = await getSearchPerformanceReport({
  projectId: "proj_123",
  dateRange: { start: "2024-01-01", end: "2024-01-31" },
  device: "mobile",
});

console.log(report.totals);

```

## Summary

- **Authentication constants** live in [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts), defining OAuth scopes and the GSC provider ID
- **Core integration logic** resides in [`src/server/features/gsc/services/GscService.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/gsc/services/GscService.ts), handling grants, connections, queries, and errors
- **API endpoints** in [`src/serverFunctions/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/gsc.ts) expose grant status, site listing, and connection management to the frontend
- **Performance data** flows through [`src/serverFunctions/searchPerformance.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/searchPerformance.ts) and MCP tools using the same service layer
- All data retrieval uses **server-side OAuth tokens** with `GscConnectionRepository` managing persistent project-to-property mappings

## Frequently Asked Questions

### What OAuth scopes does OpenSEO require for Google Search Console access?

OpenSEO requires standard Google Search Console API scopes defined in [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts), which typically include read-only access to search analytics data and site management permissions. The application uses these scopes solely to query `searchAnalytics.query` and `inspectUrl` endpoints, never persisting raw Google data beyond the connection metadata stored via `GscConnectionRepository`.

### How does OpenSEO handle expired or revoked OAuth tokens?

The `GscService` implements `isExpectedGrantFailure` to detect 401 and 403 responses or token-related errors. When encountered, these errors surface to the frontend as expected grant failures rather than internal server errors, triggering the reconnection UI. Users must then re-authorize through `startSelfHostedGscLink` or the standard OAuth flow to restore access.

### Can I connect multiple Google Search Console properties to a single OpenSEO project?

No, the architecture enforces a **one-to-one mapping** between a project and a GSC property. The `setSite` method in `GscService` binds one specific `siteUrl` to a given `projectId`, and `getConnection` retrieves only that singular relationship. To analyze multiple properties, you must create separate OpenSEO projects for each GSC site.

### Where does OpenSEO store Google Search Console connection data?

Connection metadata persists in the database through `GscConnectionRepository`, storing only the project-to-property mapping (project ID, account ID, and site URL). Raw search analytics data is never persisted; instead, `getPerformance` fetches fresh data from Google's API on each request using the stored OAuth grant, ensuring reports always reflect current GSC metrics.