# How to Set Up Rank Tracking in OpenSEO: Complete Configuration Guide

> Learn how to set up rank tracking in OpenSEO with our comprehensive guide. Configure your API, add keywords, and schedule checks for optimal SEO performance.

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

---

**To set up rank tracking in OpenSEO, configure your DataForSEO API key in `.env`, create a rank-tracking configuration in [`src/db/schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema.ts) via the repository layer, add keywords, and choose between live or scheduled checks.**

OpenSEO's rank-tracking feature monitors domain positions for target keywords across desktop and mobile devices over time. This guide walks through the complete implementation based on the `every-app/open-seo` source code, covering API configuration, database schema, repository patterns, and scheduling logic.

## Prerequisites: DataForSEO API Configuration

Before creating configurations, you need a valid **DataForSEO API key**. The rank-tracking engine relies on DataForSEO's live or queued SERP endpoints to fetch actual search results.

### Step 1: Configure Environment Variables

Copy the example environment file and add your credentials:

```bash
cp .env.example .env

```

Edit `.env` to include:

```env
DATAFORSEO_API_KEY=your-key-here

```

Use a sandbox key for local development. The application reads this value during initialization to authenticate all rank-tracking API calls.

*Source:* [`.env.example`](https://github.com/every-app/open-seo/blob/main/.env.example)

## Creating Rank-Tracking Configurations

A rank-tracking configuration ties together a domain, geographic location, device preferences, schedule, and keyword list.

### Database Schema

The configuration schema lives in **[`src/db/schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema.ts)** with two core tables:

- **`rankTrackingConfigs`** — stores domain, location, schedule interval, and metadata
- **`rankTrackingKeywords`** — stores individual keywords linked to a config

### Repository Layer Implementation

The **`RankTrackingRepository`** class in [`src/server/features/rank-tracking/repositories/RankTrackingRepository.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/rank-tracking/repositories/RankTrackingRepository.ts) handles validation and persistence. Key methods include:

- `create()` — validates enums from [`src/types/schemas/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/rank-tracking.ts) and inserts config
- `addKeywords()` — bulk inserts keywords with volume, difficulty, and CPC metrics
- `updateNextCheckAt()` — computes the next scheduled run time

### API Example: Creating a Configuration

```ts
import { db } from "@/db";
import { rankTrackingConfigs } from "@/db/schema";

const payload = {
  projectId: "proj-123",
  domain: "example.com",
  locationCode: 2840,          // United States
  languageCode: "en",
  devices: "both",             // desktop + mobile
  serpDepth: 20,               // results to fetch per page
  scheduleInterval: "weekly",  // daily | weekly | monthly | manual
};

const [newConfig] = await db
  .insert(rankTrackingConfigs)
  .values({
    id: crypto.randomUUID(),
    ...payload,
    isActive: true,
    nextCheckAt: null,
    createdAt: new Date(),
    lastCheckedAt: null,
  })
  .returning();

```

The repository layer wraps this logic with additional validation against [`src/types/schemas/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/rank-tracking.ts) enums.

*Sources:* [[`src/db/schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema.ts)](https://github.com/every-app/open-seo/blob/main/src/db/schema.ts) • [[`src/server/features/rank-tracking/repositories/RankTrackingRepository.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/rank-tracking/repositories/RankTrackingRepository.ts)](https://github.com/every-app/open-seo/blob/main/src/server/features/rank-tracking/repositories/RankTrackingRepository.ts)

## Adding Keywords to Track

After creating a configuration, populate it with target keywords. Each keyword stores SEO metrics for reference.

```ts
import { rankTrackingKeywords } from "@/db/schema";

const keywords = [
  { keyword: "seo audit tool", volume: 18100, kd: 64, cpc: 9.4 },
  { keyword: "best rank tracker", volume: 8100, kd: 58, cpc: 7.2 },
];

await db
  .insert(rankTrackingKeywords)
  .values(
    keywords.map(k => ({
      id: crypto.randomUUID(),
      configId: newConfig.id,
      ...k,
      metricsFetchedAt: null,
    }))
  );

```

The `configId` foreign key links keywords to their parent configuration. The `metricsFetchedAt` timestamp tracks when keyword difficulty and volume data were last refreshed.

## Running and Scheduling Rank Checks

OpenSEO supports two execution modes:

| Mode | Method | Use Case | Cost |
|------|--------|----------|------|
| **On-demand** | Live API call | Immediate results, testing | Higher per-check |
| **Scheduled** | Queued API call | Ongoing monitoring | Lower per-check |

### Schedule Calculation

The `computeNextCheckAt` function in **[`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts)** calculates the next run time based on `scheduleInterval`. Supported intervals: `daily`, `weekly`, `monthly`, `manual`.

### Cost Estimation

Before committing to a schedule, estimate monthly costs:

```ts
import { estimateScheduledRankCheckCredits } from "@/shared/rank-tracking";

const { monthlyCostUsd, monthlyCostCredits } = estimateScheduledRankCheckCredits(
  20,           // keywordCount
  "both",       // devices: "desktop" | "mobile" | "both"
  20,           // serpDepth
  "weekly"      // interval
);

console.log(`≈ $${monthlyCostUsd} / ${monthlyCostCredits} credits per month`);

```

The shared module enforces limits including **`MAX_KEYWORDS_PER_CONFIG = 1000`** to prevent runaway API usage.

*Source:* [[`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts)](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts)

## Local Development with Demo Data

Test the rank-tracking UI without consuming real API credits using the provided seed script.

### Step-by-Step Seeding

```bash

# Install dependencies

pnpm install

# Initialize local database (SQLite/D1)

pnpm db:migrate:local

# Generate synthetic rank-tracking data

pnpm seed:rank-tracking

```

Optional flags customize the demo:

- `--domain` — target domain for the demo config
- `--runs` — number of historical check dates to generate
- `--keywords` — keywords to include
- `--projectId` — existing project to attach the config

The script [`scripts/seed-rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/scripts/seed-rank-tracking.ts) creates realistic rank trends including climbers, fallers, and volatile keywords to demonstrate dashboard visualizations.

*Source:* [[`scripts/seed-rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/scripts/seed-rank-tracking.ts)](https://github.com/every-app/open-seo/blob/main/scripts/seed-rank-tracking.ts)

## Front-End Integration

The Rank Tracker UI uses React Query hooks for data fetching.

### Polling for Run Status

```ts
// src/client/features/rank-tracking/useRankRunPolling.ts
import { useQuery } from "@tanstack/react-query";

export const useLatestRun = (projectId: string, configId: string) =>
  useQuery(["rankTrackingLatestRun", projectId, configId], async () => {
    const res = await fetch(`/api/v1/rank-tracking/${configId}/latest-run`);
    return res.json();
  }, {
    refetchInterval: (data) => data?.status === "pending" ? 5000 : false,
  });

```

This hook automatically polls every 5 seconds while a scheduled run is pending, updating the UI when results arrive.

*Source:* [[`src/client/features/rank-tracking/useRankRunPolling.ts`](https://github.com/every-app/open-seo/blob/main/src/client/features/rank-tracking/useRankRunPolling.ts)](https://github.com/every-app/open-seo/blob/main/src/client/features/rank-tracking/useRankRunPolling.ts)

## Summary

- **API Key Setup**: Add `DATAFORSEO_API_KEY` to `.env` copied from `.env.example`
- **Configuration Creation**: Use `rankTrackingConfigs` table via `RankTrackingRepository.create()` with validated enums
- **Keyword Population**: Insert into `rankTrackingKeywords` with `configId` foreign key
- **Scheduling**: Choose `live` (on-demand) or `queued` (scheduled) via `scheduleInterval` field; costs estimated via `estimateScheduledRankCheckCredits()`
- **Development**: Run `pnpm seed:rank-tracking` for synthetic data without API calls
- **Limits**: Maximum 1000 keywords per configuration enforced in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts)

## Frequently Asked Questions

### What API does OpenSEO use for rank tracking?

OpenSEO integrates with **DataForSEO's SERP API**, using either live endpoints for immediate results or queued endpoints for scheduled monitoring. The `DATAFORSEO_API_KEY` environment variable authenticates all requests. According to the source code, no alternative providers are currently supported.

### How much does rank tracking cost in OpenSEO?

Costs depend on keyword count, device coverage, SERP depth, and check frequency. Use `estimateScheduledRankCheckCredits()` from [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts) to calculate exact monthly credits and USD cost before enabling schedules. Queued checks cost less per request than live checks.

### Can I run rank tracking without a DataForSEO account?

Yes, for local development only. Run `pnpm seed:rank-tracking` to generate synthetic rank history with realistic trends. This populates `rankSnapshots` with mock data, allowing full UI exploration without API credentials or credit consumption.

### What's the maximum number of keywords I can track?

The hard limit is **1000 keywords per configuration**, defined as `MAX_KEYWORDS_PER_CONFIG` in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts). Exceeding this triggers validation errors in the repository layer. For larger tracking needs, create multiple configurations.