# How OpenSEO Integrates with Google Search Console for Rank Tracking

> Learn how OpenSEO integrates with Google Search Console to track keyword rankings. Our solution normalizes GSC API data for easy trend analysis and alerting.

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

---

**OpenSEO pulls keyword ranking data directly from Google Search Console through a thin server-side wrapper around the GSC API, storing normalized position data in a local database for trend analysis and alerting.**

The every-app/open-seo repository implements a complete rank-tracking pipeline that connects to Google Search Console (GSC) via OAuth authentication and periodic API polling. This integration enables automated monitoring of keyword positions without manual data exports, leveraging background workers to maintain up-to-date ranking history for ongoing SEO performance analysis.

## OAuth Authentication and Token Management

Authentication begins with storing a GSC OAuth token in the user's encrypted settings. According to the source code, the [`src/lib/auth-api-key.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth-api-key.ts) module handles secure storage and automatic refreshing of these tokens when they expire. This ensures that rank-tracking jobs continue to execute without manual reauthorization, maintaining uninterrupted data synchronization between OpenSEO and Google Search Console.

## The GSC API Wrapper ([`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts))

The core API communication resides in [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts), which exports the primary `fetchGscData` function. This module constructs authenticated GET requests to the Google Search Console API and normalizes the JSON response into a predictable shape for downstream consumption. It manages query parameters such as dimensions, aggregation types, and property selection (web, image, video).

Example usage of the GSC wrapper to retrieve current keyword positions:

```typescript
import { fetchGscData } from '@/shared/gsc';

async function getKeywordRank(projectId: string, keyword: string) {
  const gscResponse = await fetchGscData(projectId, {
    query: keyword,
    dimensions: ['date'],
    aggregationType: 'AVERAGE',
  });

  // Normalized result – { position: number, date: string }
  return gscResponse.data[0];
}

```

## Rank Tracking Workflow Orchestration

The [`src/serverFunctions/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/rank-tracking.ts) file contains the high-level orchestration logic that drives the ranking updates. This server function retrieves the list of keywords assigned to a project and iterates through them to fetch current position data from GSC.

The implementation follows this sequential pattern:

1. Query active keywords for the project using `getKeywordsForProject`.
2. For each keyword, invoke `fetchGscData` to retrieve the latest average position.
3. Persist results to the database via `storeRank`.

```typescript
import { getKeywordsForProject } from '@/serverFunctions/keywords';
import { storeRank } from '@/db/provider';
import { fetchGscData } from '@/shared/gsc';

export async function runRankTracking(projectId: string) {
  const keywords = await getKeywordsForProject(projectId);
  for (const kw of keywords) {
    const rankInfo = await fetchGscData(projectId, { query: kw });
    await storeRank(projectId, kw, rankInfo.position);
  }
}

```

## Database Schema for Rank History

All historical ranking data is stored in the `rankTracking` table defined in [`src/db/schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema.ts). This schema captures the project ID, keyword, position value, and timestamp, enabling the frontend to render trend charts and position alerts over time. The `storeRank` function interacts with this schema to persist the normalized data retrieved from GSC.

## Automated Scheduling with Cron Workers

A cron-style worker, configured in [`worker-configuration.d.ts`](https://github.com/every-app/open-seo/blob/main/worker-configuration.d.ts), triggers the rank-tracking endpoint at user-defined intervals (defaulting to daily). This background processing ensures that GSC data remains fresh without requiring manual user intervention or active browser sessions.

## Frontend Data Consumption

The user interface retrieves historic trends via TanStack Server Functions defined in [`src/serverFunctions/keywords.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/keywords.ts). These functions query the `rankTracking` table to populate charts and alert dashboards, presenting GSC data as actionable SEO insights for project monitoring.

## Error Handling and API Resilience

All GSC API calls are wrapped with error-handling utilities from [`src/shared/error-codes.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts). This module translates HTTP 4xx and 5xx responses into user-friendly messages such as "Insufficient permissions" or "Quota exceeded," preventing cryptic API errors from reaching the end user while providing actionable feedback for troubleshooting integration issues.

## Summary

- **Authentication** is handled securely in [`src/lib/auth-api-key.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth-api-key.ts) with automatic OAuth token refreshing.
- **API communication** is abstracted through the `fetchGscData` function in [`src/shared/gsc.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/gsc.ts), which normalizes GSC responses.
- **Orchestration** occurs in [`src/serverFunctions/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/rank-tracking.ts), which coordinates keyword retrieval and data storage.
- **Persistence** uses the `rankTracking` table defined in [`src/db/schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema.ts) to maintain historical position data.
- **Scheduling** leverages cron workers configured in [`worker-configuration.d.ts`](https://github.com/every-app/open-seo/blob/main/worker-configuration.d.ts) for automated daily updates.
- **Error handling** in [`src/shared/error-codes.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts) maps API failures to user-friendly messages.

## Frequently Asked Questions

### How does OpenSEO authenticate with Google Search Console?

OpenSEO uses OAuth tokens that are encrypted and stored in user settings via [`src/lib/auth-api-key.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth-api-key.ts). The system automatically refreshes these tokens when they expire, ensuring continuous access to GSC data without requiring manual reauthorization from the user.

### How frequently does OpenSEO update rank tracking data?

By default, a cron-style worker configured in [`worker-configuration.d.ts`](https://github.com/every-app/open-seo/blob/main/worker-configuration.d.ts) invokes the rank-tracking endpoint daily. Users can customize this interval to balance data freshness against API quota limits based on their specific monitoring needs.

### Where does OpenSEO store the ranking data retrieved from GSC?

All ranking data is persisted in the `rankTracking` table defined in [`src/db/schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema.ts). This database structure stores project IDs, keywords, position values, and timestamps, enabling the frontend to query historical trends and generate performance charts.

### What happens when the GSC API returns an error or quota limit?

Error handling utilities in [`src/shared/error-codes.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts) intercept HTTP 4xx and 5xx responses from the GSC API. These utilities translate technical error codes into user-friendly messages like "Quota exceeded" or "Insufficient permissions," allowing users to understand and resolve integration issues quickly.