# How Rank Tracking Is Implemented in OpenSEO: Architecture and Code Walkthrough

> Explore OpenSEO's rank tracking architecture. Discover how PostgreSQL, Zod, business logic, and API endpoints combine with DataForSEO for efficient implementation.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: architecture
- Published: 2026-08-30

---

**OpenSEO implements rank tracking through a four-layer architecture that combines PostgreSQL schema definitions, Zod validation schemas, pure business-logic utilities, and API endpoints orchestrating DataForSEO service calls.**

The `every-app/open-seo` repository provides a production-ready implementation of SEO rank tracking that balances cost efficiency with scheduling flexibility. Understanding how rank tracking is implemented in this TypeScript codebase reveals a modular design that separates database concerns, validation logic, and external API orchestration into discrete, testable units.

## Database Schema for Rank Tracking Configurations

The foundation of rank tracking in OpenSEO lives in [`src/db/schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema.ts), which defines the `rank_tracking_configs` table. This table stores both static configuration parameters and runtime state fields.

Each configuration row tracks the target domain, location settings, device preferences (desktop, mobile, or both), SERP crawl depth, and check frequency (daily, weekly, or monthly). Runtime fields include `nextCheckAt` for scheduling and `lastSkipReason` for debugging execution failures.

This declarative approach allows the system to treat rank tracking as configuration-driven background jobs rather than hardcoded processes.

## Validation Layer with Zod Schemas

Before any database operations occur, OpenSEO validates all incoming payloads using Zod schemas defined in [`src/types/schemas/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/rank-tracking.ts). This layer prevents malformed data from reaching the business logic or external APIs.

The module exports four primary schemas:
- **`createConfigSchema`** – Validates new rank tracking configurations
- **`updateConfigSchema`** – Validates partial updates to existing configs
- **`triggerCheckSchema`** – Validates manual check requests
- **`estimateCostSchema`** – Validates cost estimation requests

This validation strategy ensures type safety across the API surface while providing clear error messages to API consumers.

## Business Logic Utilities in src/shared/rank-tracking.ts

All core calculations and transformations live in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts). This module contains pure functions that handle cost estimation, scheduling, and UI formatting without side effects.

### Cost Estimation Functions

The `estimateRankCheckCredits` and `estimateScheduledRankCheckCredits` functions calculate both USD and credit costs based on four parameters: keyword count, device count, SERP depth, and execution method (`live` or `queued`).

```typescript
import { estimateRankCheckCredits } from '@/shared/rank-tracking';
import type { RankTrackingConfig } from '@/types/schemas/rank-tracking';

const config: RankTrackingConfig = {
  devices: 'both',
  serpDepth: 30,
  // other required fields...
};

const { costUsd, costCredits } = estimateRankCheckCredits(
  150,           // keywordCount
  config.devices,
  config.serpDepth,
  'live',        // method: 'live' for immediate checks
);

console.log(`Live check will cost $${costUsd} (${costCredits} credits)`);

```

Live checks cost more than queued checks because they bypass the batching optimization. The `estimateScheduledRankCheckCredits` function specifically handles the queued method for background jobs.

### Scheduling Logic

The `computeNextCheckAt` function generates ISO 8601 timestamps for the next execution based on the schedule type (daily, weekly, or monthly). It handles timezone drift and implements randomized execution windows to prevent thundering herd problems against the DataForSEO API.

```typescript
import { computeNextCheckAt } from '@/shared/rank-tracking';

const nextRun = computeNextCheckAt('weekly');
console.log('Next weekly check scheduled for:', nextRun);

```

This function appears in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts) around lines 78-95 and 100-124, implementing drift compensation to ensure checks run at consistent local times regardless of daylight saving changes.

### UI Label Helpers

The module exports formatting utilities including `devicesLabel`, `scheduleLabel`, and `devicesCount`. These convert database enums into human-readable strings for dashboard displays and calculate the numeric device multiplier used in cost equations.

## API Routes and Execution Flow

The route tree defined in [`src/routeTree.gen.ts`](https://github.com/every-app/open-seo/blob/main/src/routeTree.gen.ts) wires HTTP endpoints to service functions that coordinate the validation, database, and DataForSEO layers.

A typical rank check flow follows this sequence:
1. The request body validates against Zod schemas from [`src/types/schemas/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/rank-tracking.ts)
2. The service creates or updates a row in `rank_tracking_configs`
3. For manual triggers, the service calls the DataForSEO **live** endpoint immediately
4. For scheduled execution, the system enqueues a **queued** task using the cost-effective batch API
5. The billing module ([`src/shared/billing.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/billing.ts)) applies charges using the `AUTUMN_SEO_DATA_CREDITS_PER_USD` conversion rate

This separation allows the same cost estimation logic to preview expenses before execution while ensuring users cannot exceed their credit balance.

## Background Task Execution and Error Handling

Scheduled rank checks execute via background workers that poll active configurations. The worker uses `computeNextCheckAt` to determine eligibility, then calls DataForSEO via the queued method for cost efficiency.

When checks cannot execute, the system records a `RankTrackingSkipReason` in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts). The three possible values are:
- **`plan_required`** – No active subscription exists
- **`no_keywords`** – Configuration lacks associated keywords
- **`insufficient_credits`** – Cost estimation exceeds available balance

These skip reasons appear in the `lastSkipReason` column of `rank_tracking_configs`, providing transparency for debugging failed executions.

## Summary

OpenSEO implements rank tracking through a clean separation of concerns that prioritizes testability and cost transparency:

- **Database layer** stores configuration and state in `rank_tracking_configs` (defined in [`src/db/schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema.ts))
- **Validation layer** uses Zod schemas in [`src/types/schemas/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/rank-tracking.ts) to enforce type safety
- **Business logic layer** provides pure functions for cost estimation (`estimateRankCheckCredits`), scheduling (`computeNextCheckAt`), and formatting in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts)
- **API layer** routes requests through the generated tree in [`src/routeTree.gen.ts`](https://github.com/every-app/open-seo/blob/main/src/routeTree.gen.ts), choosing between live and queued DataForSEO execution methods
- **Billing integration** converts USD estimates to credit deductions via [`src/shared/billing.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/billing.ts)

## Frequently Asked Questions

### How does OpenSEO calculate the cost of a rank tracking check?

OpenSEO calculates costs through the `estimateRankCheckCredits` function in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts). The calculation multiplies keyword count by device count (1 for single device, 2 for both) and SERP depth, then applies different rates for `live` versus `queued` execution methods. The [`billing.ts`](https://github.com/every-app/open-seo/blob/main/billing.ts) module then converts the USD estimate into platform credits using the `AUTUMN_SEO_DATA_CREDITS_PER_USD` constant.

### What is the difference between live and queued rank checks?

Live checks execute immediately against the DataForSEO API at premium pricing, suitable for on-demand validation. Queued checks execute through the batch processing endpoint at lower cost, making them ideal for scheduled monitoring. The `estimateRankCheckCredits` function accepts a method parameter to distinguish between these modes, while the API routes automatically select the appropriate endpoint based on whether the request is manual or scheduled.

### How does OpenSEO handle scheduling for recurring rank checks?

The `computeNextCheckAt` function in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts) handles all scheduling logic. It accepts a schedule type ('daily', 'weekly', or 'monthly') and returns the next ISO 8601 timestamp. The implementation includes drift compensation to maintain consistent local execution times and randomization to prevent API rate limiting. Background workers reference this timestamp to determine when to enqueue the next check.

### What happens when a rank tracking job fails due to insufficient credits?

When cost estimation exceeds the available balance, the system records `insufficient_credits` as the `RankTrackingSkipReason` in the configuration's `lastSkipReason` field. Other skip reasons include `plan_required` (missing subscription) and `no_keywords` (empty keyword list). These values are defined in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts) and allow the dashboard to display specific failure explanations without exposing internal error details.