# How OpenSEO Implements Scheduled Rank Tracking with Cloudflare Workflows

> Discover how OpenSEO uses scheduled Cloudflare Workflows to automate rank tracking. Learn about validation, credit checks, and keyword queuing with the DataForSEO API.

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

---

**OpenSEO performs automated rank tracking through a Cloudflare Workflow that executes on a Cron schedule, validating runs, checking billing credits, and queuing keyword checks via the DataForSEO API.**

OpenSEO is an open-source SEO platform that leverages **Cloudflare Workflows** to automate keyword position monitoring without maintaining dedicated infrastructure. The system uses a cron-triggered workflow defined in [`src/server/workflows/RankCheckWorkflow.ts`](https://github.com/every-app/open-seo/blob/main/src/server/workflows/RankCheckWorkflow.ts) to orchestrate daily rank checks, handling everything from credit validation to incremental result persistence. This architecture separates scheduled batch processing from manual live checks to optimize API costs and execution reliability.

## Cron Schedule Configuration in wrangler.jsonc

The workflow trigger is defined in the Cloudflare Workers configuration file at the repository root. The **Cron trigger** is declared under the `triggers.crons` array in `wrangler.jsonc`, specifying when Cloudflare should automatically invoke the workflow.

When the cron expression fires (for example, `"0 0 * * *"` for daily midnight UTC), Cloudflare creates a workflow invocation that resolves to the `RankCheckWorkflow` entry-point with the **trigger** parameter set to `"scheduled"`.

```json
{
  "name": "open-seo",
  "type": "javascript",
  "compatibility_date": "2024-09-01",
  "triggers": {
    "crons": [
      "0 0 * * *"
    ]
  },
  "vars": {
    "RANK_CHECK_TRIGGER": "scheduled"
  }
}

```

## The RankCheckWorkflow Orchestration Logic

The core workflow implementation lives in [`src/server/workflows/RankCheckWorkflow.ts`](https://github.com/every-app/open-seo/blob/main/src/server/workflows/RankCheckWorkflow.ts). At line 46, the workflow checks the trigger type to determine execution mode. For **scheduled triggers**, it routes to `runQueuedCheck` (line 56); for manual invocations, it uses `runLiveCheck` (line 58).

The workflow executes a six-step pipeline that ensures reliable, cost-effective rank tracking:

### Step 1: Run Validation

Before processing keywords, the workflow validates that the associated `RankTrackingRepository` run is still active. It checks that `run.status` is neither `failed` nor `completed`, preventing duplicate or stale executions from consuming resources.

### Step 2: Keyword Preparation and Cost Estimation

The workflow loads all keywords associated with the requested configuration, optionally filtering by supplied IDs. It then calls `estimateRankCheckCredits` (lines 88-94) to calculate the total credit cost based on keyword volume, device types, and SERP depth.

### Step 3: Credit Validation via Autumn

For deployments running on hosted Cloudflare Access servers, the workflow queries the **Autumn** billing service (lines 101-124) to verify that the user’s balance covers the estimated cost. If credits are insufficient, the workflow terminates early to prevent API charges.

### Step 4: Executing the Check

The workflow routes execution based on trigger type:

- **Scheduled runs**: Calls `runQueuedCheck` (implemented in [`src/server/workflows/rankCheckPaths.ts`](https://github.com/every-app/open-seo/blob/main/src/server/workflows/rankCheckPaths.ts)), which submits keywords to DataForSEO's **task queue**. This asynchronous approach costs approximately 30% of a live check and is ideal for bulk monitoring.
- **Manual runs**: Calls `runLiveCheck` for immediate synchronous results, useful for on-demand validation.

```typescript
// Cloudflare invokes the workflow with trigger: "scheduled"
// The workflow routes to runQueuedCheck for cost-efficient batch processing
await runQueuedCheck(step, {
  client,
  keywords,
  devices,
  serpDepth,
  domain,
  locationCode,
  languageCode,
  locationName,
  runId,
});

```

### Step 5: Result Collection

As the workflow progresses, it writes each batch of results to the `rank_tracking_snapshots` table. This incremental persistence strategy prevents data loss if the workflow encounters errors during long-running checks with thousands of keywords.

### Step 6: Finalization and Event Tracking

Upon completion, the workflow updates the run status to `completed`, writes a summary line to the logs, and fires a PostHog analytics event via `captureServerEvent` (lines 104-126). This provides observability into execution success and credit consumption.

## Error Handling and Reliability

If any step throws an exception, the workflow invokes `markRankCheckRunFailed` (lines 29-49) to update the database record with the specific failure reason. This ensures that stale or erroneous runs are clearly marked, preventing the cron schedule from attempting to reprocess corrupted configurations indefinitely.

## Database Schema and Persistence

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 all database operations. The schema is defined in [`src/types/schemas/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/rank-tracking.ts), which structures the `rank_tracking_snapshots` table for time-series ranking data and the configuration tables for keyword lists.

## Summary

- **Cloudflare Workflows** execute rank tracking via Cron triggers defined in `wrangler.jsonc`, automatically invoking the workflow at specified intervals.
- **[`RankCheckWorkflow.ts`](https://github.com/every-app/open-seo/blob/main/RankCheckWorkflow.ts)** orchestrates the process through six distinct steps: validation, preparation, billing checks, execution, persistence, and finalization.
- **Cost optimization** is achieved through `runQueuedCheck`, which uses DataForSEO's task queue at roughly 30% the cost of live checks for scheduled monitoring.
- **Billing validation** occurs via the Autumn service before API calls are initiated, preventing overage charges for hosted deployments.
- **Data integrity** is maintained by writing results incrementally to `rank_tracking_snapshots` and marking failed runs explicitly via `markRankCheckRunFailed`.

## Frequently Asked Questions

### How does OpenSEO schedule automated rank tracking checks?

OpenSEO uses Cloudflare's **Cron triggers** defined in the `wrangler.jsonc` configuration file. The `triggers.crons` array specifies the schedule (e.g., `"0 0 * * *"` for daily execution), and Cloudflare automatically invokes `RankCheckWorkflow` with the trigger parameter set to `"scheduled"` at those intervals.

### What is the difference between runQueuedCheck and runLiveCheck?

**`runQueuedCheck`** submits keywords to DataForSEO's asynchronous task queue, making it cost-effective (approximately 30% of live-check pricing) and suitable for scheduled bulk monitoring. **`runLiveCheck`** performs synchronous API calls for immediate results, designed for manual, on-demand rank checks where latency is acceptable but cost is higher.

### How does the workflow prevent excessive API charges?

Before executing checks, the workflow calls `estimateRankCheckCredits` to calculate the total cost, then queries the **Autumn** billing service to verify sufficient user balance. If credits are insufficient, the workflow terminates before invoking paid DataForSEO APIs, protecting users from accidental overages.

### What happens if a scheduled rank tracking run fails?

The workflow catches exceptions and invokes `markRankCheckRunFailed` (lines 29-49) to update the run status to `failed` and record the specific error reason in the database. This prevents the cron schedule from attempting to reprocess the same corrupted configuration on the next execution cycle.