# How OpenSEO Manages Batch Processing and Keyword Limits for Rank Tracking

> Discover how OpenSEO optimizes rank tracking with batch processing of 10 keywords and limits of 1,000 keywords per configuration. Learn about API cost calculation and project limits.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: internals
- Published: 2026-06-26

---

**OpenSEO processes rank-tracking requests in fixed batches of 10 keywords, enforces strict limits of 1,000 keywords per configuration and 20 configurations per project, and applies device-specific multipliers to calculate accurate API costs before submitting requests to DataForSEO.**

The open-source repository `every-app/open-seo` implements a defensive architecture for rank tracking that prevents API quota exhaustion through hard-coded batch processing and keyword limits. These constraints are defined in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts) and enforced across the scheduler, UI validation, and cost estimation modules. Understanding these limits reveals how the platform maintains predictable performance while scaling to thousands of tracked keywords.

## Batch Processing Architecture

### Fixed Batch Sizes of 10 Keywords

The core batching mechanism relies on the `KEYWORDS_PER_BATCH` constant set to **10** in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts) (line 30). When the system executes a rank check, it divides the total keyword set into chunks of exactly 10 keywords, regardless of the total volume. This fixed size ensures that each API call to DataForSEO remains within optimal payload limits while allowing the background worker to process keywords incrementally.

The batching logic appears in the cost estimation and scheduling layers. For example, when validating a bulk keyword upload, the code splits arrays using this constant:

```typescript
import { KEYWORDS_PER_BATCH, MAX_KEYWORDS_PER_CONFIG } from "@/shared/rank-tracking";

// Example: validating a bulk keyword upload
function validateUpload(keywords: string[]) {
  if (keywords.length > MAX_KEYWORDS_PER_CONFIG) {
    throw new Error(
      `You can only track up to ${MAX_KEYWORDS_PER_CONFIG} keywords per config.`,
    );
  }
  // Split into batches for API calls
  const batches = [];
  for (let i = 0; i < keywords.length; i += KEYWORDS_PER_BATCH) {
    batches.push(keywords.slice(i, i + KEYWORDS_PER_BATCH));
  }
  return batches; // each batch will be sent to DataForSEO
}

```

### Scheduling and Timing Constraints

Each batch is allocated **6 seconds** of processing time, defined by `SECONDS_PER_BATCH = 6` at line 33 of [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts). This value drives the scheduler's rate-limiting logic, allowing the system to estimate queue depth and prevent job overlap. By multiplying the number of batches by this constant, OpenSEO predicts total execution time and communicates expected completion windows to users.

## Keyword and Configuration Limits

### Per-Configuration Keyword Caps

OpenSEO enforces a hard limit of **1,000 keywords** per rank-tracking configuration via `MAX_KEYWORDS_PER_CONFIG = 1000` (line 36). A configuration represents a unique domain and location combination. The UI applies this constraint during manual entry and bulk import operations, rejecting uploads that exceed the threshold. This limit prevents individual configurations from monopolizing background worker threads and ensures fair resource distribution across projects.

### Project-Level Configuration Constraints

At the project level, the system caps configurations at **20 per project** using `MAX_CONFIGS_PER_PROJECT = 20` (line 39). Since each configuration can track up to 1,000 keywords, this effectively allows 20,000 keywords per project. This constraint protects the scheduler from being overwhelmed by projects with excessive location or domain variants, maintaining predictable latency for all users.

## Cost-Aware Batching Logic

### Device Multipliers and SERP Requests

Before batching occurs, OpenSEO calculates the total SERP request volume by multiplying keyword count against device settings. The `devicesCount` helper function (lines 28-31 in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts)) converts the device setting—`desktop`, `mobile`, or `both`—into a numeric multiplier of 1 or 2. When a user selects "both," each keyword generates two distinct SERP requests, doubling the batch count required.

The total request count follows the formula: `keywordCount × devicesCount(devices)`. These requests are then grouped into batches of 10 for actual API transmission.

### Credit Estimation with `estimateRankCheckCredits`

The `estimateRankCheckCredits` function (lines 62-73) applies the cost model to inform users of expected expenses before execution. It accepts the keyword count, device setting, search depth, and API priority level, then returns both credit and USD estimates:

```typescript
import { estimateRankCheckCredits } from "@/shared/rank-tracking";

async function showCostPreview(
  keywordCount: number,
  devices: "desktop" | "mobile" | "both",
  depth: number,
) {
  const { costUsd, costCredits } = estimateRankCheckCredits(
    keywordCount,
    devices,
    depth,
    "queued", // scheduled runs use the cheaper queued API
  );
  console.log(`Estimated cost: $${costUsd.toFixed(2)} (${costCredits} credits)`);
}

```

This estimation relies on the same batch sizing logic used during actual execution, ensuring that previewed costs match actual billing.

## Validation and Data Flow

The batch processing and keyword limits propagate through multiple system layers. The schema definitions in [`src/types/schemas/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/types/schemas/rank-tracking.ts) enforce structural validation, while the server-side retrieval logic in [`src/server/features/rank-tracking/services/rankTrackingResults.ts`](https://github.com/every-app/open-seo/blob/main/src/server/features/rank-tracking/services/rankTrackingResults.ts) assumes batched data structures when aggregating SERP results. Client-side hooks like [`src/client/features/rank-tracking/useRankRunPolling.ts`](https://github.com/every-app/open-seo/blob/main/src/client/features/rank-tracking/useRankRunPolling.ts) and [`src/client/features/rank-tracking/useRankCheckTrigger.ts`](https://github.com/every-app/open-seo/blob/main/src/client/features/rank-tracking/useRankCheckTrigger.ts) respect the 6-second batch timing when polling for updates or triggering manual checks.

## Summary

- **Batch sizing**: OpenSEO processes exactly 10 keywords per batch (`KEYWORDS_PER_BATCH`), as defined in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts).
- **Timing**: Each batch consumes approximately 6 seconds (`SECONDS_PER_BATCH`), driving scheduler estimates.
- **Keyword limits**: Single configurations max out at 1,000 keywords (`MAX_KEYWORDS_PER_CONFIG`).
- **Configuration limits**: Projects are capped at 20 configurations (`MAX_CONFIGS_PER_PROJECT`).
- **Cost calculation**: The `devicesCount` multiplier and `estimateRankCheckCredits` function calculate total SERP requests and credits before API calls.
- **API protection**: These constraints collectively prevent DataForSEO API overload and maintain predictable queue performance.

## Frequently Asked Questions

### What is the maximum number of keywords allowed per rank-tracking configuration in OpenSEO?

OpenSEO limits each rank-tracking configuration to **1,000 keywords** via the `MAX_KEYWORDS_PER_CONFIG` constant in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts) (line 36). This constraint applies to both manual keyword entry and bulk CSV imports, with validation occurring at the UI and API layers to prevent oversized configurations from degrading system performance.

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

The platform uses the `estimateRankCheckCredits` function to multiply the keyword count by the device multiplier (1 for single device, 2 for both desktop and mobile), then applies the batch cost model. This calculation occurs in [`src/shared/rank-tracking.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/rank-tracking.ts) and provides users with accurate credit and USD estimates before they commit to the API request.

### Why does OpenSEO restrict projects to 20 rank-tracking configurations?

The `MAX_CONFIGS_PER_PROJECT = 20` limit (line 39) prevents the background scheduler from being overwhelmed by projects that track hundreds of domain or location combinations. By capping configurations, the system ensures that batch processing queues remain predictable and that API rate limits are distributed fairly across all projects in the instance.

### How long does OpenSEO allocate for processing each keyword batch?

Each batch of 10 keywords is allocated **6 seconds** of processing time based on the `SECONDS_PER_BATCH` constant (line 33). This timing estimate drives the job scheduler's rate-limiting logic and determines how the UI calculates expected completion times for large rank-tracking runs.