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 metadatarankTrackingKeywords— 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 fromsrc/types/schemas/rank-tracking.tsand inserts configaddKeywords()— bulk inserts keywords with volume, difficulty, and CPC metricsupdateNextCheckAt()— 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_KEYto.envcopied from.env.example - Configuration Creation: Use
rankTrackingConfigstable viaRankTrackingRepository.create()with validated enums - Keyword Population: Insert into
rankTrackingKeywordswithconfigIdforeign key - Scheduling: Choose
live(on-demand) orqueued(scheduled) viascheduleIntervalfield; costs estimated viaestimateScheduledRankCheckCredits() - Development: Run
pnpm seed:rank-trackingfor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →