How OpenSEO Handles Scheduled Rank Tracking Checks and Reporting
OpenSEO uses a Cloudflare Worker cron job that runs every 5 minutes to queue due rank tracking configurations, while Cloudflare Workbooks fetch SERP data from DataForSEO and store results accessible via the MCP get_rank_tracker tool or REST API.
OpenSEO (every-app/open-seo) is an open-source SEO platform that automates rank tracking through scheduled checks and comprehensive reporting. The system coordinates a cron-based scheduler, workflow runners, and retrieval tools to monitor keyword positions across desktop and mobile devices. Understanding how these components interact helps developers optimize their rank tracking implementations and stay within API rate limits.
Scheduler Architecture: The Cron Worker
The scheduling system resides in src/server/features/rank-tracking/services/scheduledRankChecks.ts and operates as a Cloudflare Worker that executes every 5 minutes. This worker identifies which rank tracking configurations are due for checking and manages execution constraints to prevent overwhelming external APIs.
Fetching Due Configurations
The scheduler begins by calling RankTrackingRepository.getDueConfigsWithOrganization() to retrieve all configurations where next_check_at has passed. Each configuration includes the associated organization's billing status, allowing the system to enforce plan restrictions before executing costly API calls.
The repository method returns complete tracking profiles including:
schedule_interval(daily, weekly, monthly, or manual)devicesarray (desktop and/or mobile)serp_depthfor result pagination- Current
next_check_attimestamps
Budget Enforcement and Rate Limiting
To respect DataForSEO's rate limits, the scheduler implements strict resource budgeting. Each tick operates with a SCHEDULED_TASK_UNIT_BUDGET of 1,000 task units, calculated as keywords multiplied by devices. The system also enforces a TICK_DEADLINE_MS of 3 minutes to ensure the worker completes before the next invocation.
The loop terminates when either:
- The task unit budget is exhausted
- The 3-minute deadline approaches
- No more due configurations remain
Plan Verification and Atomic Claiming
For hosted deployments, the scheduler verifies the organization has a paid plan via customerHasPaidPlan(). Free-tier configurations are skipped and logged with skippedFree status.
Eligible configurations are claimed atomically using RankTrackingRepository.claimDueConfig(), which:
- Updates
next_check_atbased on theschedule_interval - Clears any previous
last_skip_reason - Prevents race conditions between concurrent worker instances
After claiming, the scheduler calls beginRankCheckRun() to launch the Cloudflare Workflow. If a workflow is already running for that configuration, the scheduler restores the original schedule and records the conflict.
The Rank Check Runner: Cloudflare Workflows
When beginRankCheckRun() initiates in src/server/features/rank-tracking/services/RankTrackingService.ts, it launches the RANK_CHECK_WORKFLOW Cloudflare Workflow. This workflow handles the actual SERP data retrieval:
- Fetches ranking data from the DataForSEO API for every keyword/device combination
- Stores results in the database with timestamps
- Records a unique run ID for later querying
- Updates
last_checked_atupon completion
The same RankTrackingService.triggerCheck() method serves both scheduled runs and manual triggers from the run_rank_tracker MCP tool, ensuring consistent behavior regardless of initiation source.
Reporting and Data Retrieval
OpenSEO exposes rank tracking data through multiple interfaces, allowing both programmatic access via MCP tools and human-readable dashboards.
MCP Tool Interface
The get_rank_tracker tool (defined following the pattern in src/server/mcp/tools/run-rank-tracker.ts) returns structured data including:
trackerId: Unique identifier for the configurationlastCheckedAt: ISO timestamp of the most recent completionkeywords: Array containing each keyword's currentrankandrankChangescheduleInterval: Current frequency settingdevices: Active device targetsserpDepth: Configured result depthlastSkipReason: Explanation if last run was skipped
REST API Endpoints
The public API exposes endpoints like /api/rank-tracking/:id that return the same dataset consumed by the frontend. The web/src/routes/api/backlink-check.ts file demonstrates similar rank data handling patterns used throughout the platform.
Frontend Visualization
The rank tracking UI in src/web/src/routes/_marketing/features/rank-tracking/ renders this data with:
- Movement arrows indicating rank changes
- SERP feature flags (featured snippets, local packs, etc.)
- Historical trend charts
- Device-specific breakdowns
Implementation Examples
Creating a Scheduled Configuration
import { RankTrackingRepository } from '../features/rank-tracking/repositories/RankTrackingRepository';
const tracker = await RankTrackingRepository.create({
projectId: "proj_123",
domain: "example.com",
devices: ["desktop", "mobile"],
scheduleInterval: "daily",
serpDepth: 10,
keywords: ["seo tools", "rank tracker"]
});
// Returns a config with next_check_at set based on the interval
Triggering Manual Checks
// Using the MCP tool for immediate checks
import { runRankTrackerTool } from '../mcp/tools/run-rank-tracker';
const result = await runRankTrackerTool.handler({
projectId: "proj_123",
trackerId: "c0f2b9e4-8d3a-4b5e-9c1f-2d3e4f5a6b7c",
maxCostCredits: 500,
});
// Returns workflow instance ID and estimated cost
Retrieving Current Rankings
// Using the get_rank_tracker MCP tool
const report = await getRankTrackerTool.handler({
projectId: "proj_123",
trackerId: "c0f2b9e4-8d3a-4b5e-9c1f-2d3e4f5a6b7c",
});
/* Report structure:
{
trackerId: "c0f2b9e4-8d3a-4b5e-9c1f-2d3e4f5a6b7c",
lastCheckedAt: "2024-07-12T08:15:00Z",
keywords: [
{ keyword: "seo tools", rank: 3, rankChange: +1 },
{ keyword: "rank tracker", rank: 7, rankChange: -2 }
],
scheduleInterval: "daily",
devices: ["desktop", "mobile"],
serpDepth: 10,
lastSkipReason: null
}
*/
Summary
- Scheduled checks rely on a Cloudflare Worker cron job running every 5 minutes from
src/server/features/rank-tracking/services/scheduledRankChecks.ts - Budget protection enforces a 1,000 task-unit limit per tick and 3-minute execution deadlines to respect DataForSEO rate limits
- Atomic claiming via
RankTrackingRepository.claimDueConfig()prevents duplicate executions and handlesnext_check_atscheduling - Workflow execution uses
beginRankCheckRun()inRankTrackingService.tsto launch Cloudflare Workflows that fetch SERP data - Reporting combines MCP tools (
get_rank_tracker), REST APIs, and frontend dashboards displaying rank changes and SERP features
Frequently Asked Questions
How often does OpenSEO check rankings?
OpenSEO checks rankings based on the schedule_interval set in each configuration: daily, weekly, or monthly. The scheduler worker runs every 5 minutes to identify due configurations, but actual API calls respect the user-selected interval. Manual checks can be triggered instantly via the run_rank_tracker MCP tool regardless of schedule settings.
What happens if a rank check is already running?
If beginRankCheckRun() detects an existing workflow instance for the same tracker, it restores the original next_check_at schedule and records the conflict without initiating duplicate API calls. This prevents double-billing and data corruption while preserving the next scheduled check time.
How does OpenSEO handle API rate limits?
The scheduler enforces a SCHEDULED_TASK_UNIT_BUDGET of 1,000 units per tick (keywords × devices) and a 3-minute TICK_DEADLINE_MS. When limits are reached, remaining configurations wait for the next 5-minute interval. The system also verifies paid plan status via customerHasPaidPlan() before executingDataForSEO calls to ensure credit availability.
Can I retrieve historical rank data programmatically?
Yes. While the get_rank_tracker MCP tool returns the most recent check results including lastCheckedAt and current rank values with rankChange deltas, the underlying database schema in src/db/app.schema.ts stores historical records. Query the rank_tracking_configs table directly or use the REST API endpoints to build custom time-series reports showing position trends over months of scheduled checks.
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 →