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

To set up rank tracking in OpenSEO, configure your DataForSEO API key in .env, create a rank-tracking configuration in 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:

cp .env.example .env

Edit .env to include:

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

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 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 handles validation and persistence. Key methods include:

  • create() — validates enums from 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

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 enums.

Sources: [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)

Adding Keywords to Track

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

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 calculates the next run time based on scheduleInterval. Supported intervals: daily, weekly, monthly, manual.

Cost Estimation

Before committing to a schedule, estimate monthly costs:

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)

Local Development with Demo Data

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

Step-by-Step Seeding


# 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 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)

Front-End Integration

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

Polling for Run Status

// 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)

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

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 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. Exceeding this triggers validation errors in the repository layer. For larger tracking needs, create multiple configurations.

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 →