How DataForSEO API Billing and Rate Limiting Work in OpenSEO (2025 Implementation Guide)

OpenSEO separates authentication, billing, and rate-limit handling into distinct layers that track usage credits, apply pricing markup for hosted customers, and gracefully handle DataForSEO API 429 responses with exponential backoff.

OpenSEO's DataForSEO integration provides a production-ready foundation for SEO data workflows. The platform abstracts the complexity of credit management, error classification, and retry policies behind a unified request wrapper. This guide breaks down the exact implementation in every-app/open-seo.

DataForSEO API Architecture Overview

OpenSEO routes all DataForSEO calls through a centralized authentication and request wrapper in src/server/lib/dataforseo/core.ts. The createAuthenticatedFetch function injects the DATAFORSEO_API_KEY and enforces a 60-second request timeout.

This wrapper serves as the interception point for:

  • Authentication header injection
  • Error classification and mapping
  • Retry policy enforcement
  • Rate-limit detection

Billing Classification and Error Handling

Mapping DataForSEO Errors to Typed AppErrors

After any non-2xx response, OpenSEO classifies errors that indicate depleted account balance. The system looks for HTTP 402 status codes or messages containing "insufficient funds" and throws typed AppError instances.

The createDataforseoBillingClassifier function in src/server/lib/dataforseoBillingClassification.ts builds these classifiers per API path:

import { createDataforseoBillingClassifier } from "@/server/lib/dataforseoBillingClassification";

const classifyKeywordsError = createDataforseoBillingClassifier({
  pathPrefix: "/keywords/",
  billingIssueCode: "KEYWORDS_BILLING_ISSUE",
  billingIssueMessage: "The connected DataForSEO account has a billing or balance issue",
});

This produces error codes like:

  • BACKLINKS_BILLING_ISSUE
  • AI_BILLING_ISSUE
  • KEYWORDS_BILLING_ISSUE

Each code surfaces a clear billing problem message to the user interface.

Usage Credit Pool Management

OpenSEO tracks two distinct credit pools:

  1. Monthly usage credits — deducted first
  2. Top-up credits — used after monthly pool depletion

The conversion rate is fixed at 1 USD → 1000 DataForSEO credits. Hosted customer pricing includes a markup factor (approximately 1.28×) applied to raw DataForSEO costs before billing to the OpenSEO subscription.

Constants and conversion helpers reside in src/shared/billing.ts:

// Conversion constants
AUTUMN_SEO_DATA_CREDITS_PER_USD

// Markup application for hosted mode
applyBillingMarkupUsd(rawCost)

Self-hosted deployments bypass the OpenSEO credit pool entirely. The code checks isHostedServerAuthMode() before applying deductions, and these users pay DataForSEO directly using autumnSeoDataCreditsToUsd without markup.

Rate Limiting and Retry Policies

Handling DataForSEO 429 Responses

When DataForSEO returns HTTP 429 (RATE_LIMITED), the wrapper maps it to a generic RATE_LIMITED error code in AppError. The calling workflow decides whether to:

  • Retry the request later
  • Surface a user-friendly rate-limited message

This mapping occurs in src/server/lib/dataforseo/core.ts:

// Simplified error mapping logic
status === 429 ? "RATE_LIMITED" : /* other codes */

Per-API Retry Configuration

The same wrapper implements differentiated retry behavior:

Call Type Retry Policy Rationale
Idempotent reads (keyword data, rankings) Up to 2 retries, 250ms backoff Safe to repeat without side effects
Non-idempotent calls (Lighthouse live tests) No retries Prevents double-charging

This is controlled by DATAFORSEO_MAX_RETRIES logic within createAuthenticatedFetch.

Billing Usage Reporting with Autumn API

For hosted customers, OpenSEO queries Autumn's events.list endpoint to pull usage-credit events. The client implements exponential backoff with maximum retry count for HTTP 429 responses:

import { getBillingUsageEvents } from "@/serverFunctions/billing";

await getBillingUsageEvents({ start: 0, end: Date.now() });
// Internally backs off on 429 and retries up to 3 times

The implementation in src/serverFunctions/billing.ts includes fetchAutumnEventsPage with full retry logic.

Practical DataForSEO API Integration Example

Here's a complete pattern for making DataForSEO API calls with full billing and rate-limit handling:

import { keywordsDataApi } from "@/server/lib/dataforseo/core";
import { createDataforseoBillingClassifier } from "@/server/lib/dataforseoBillingClassification";
import { assertOk, parseTaskItems } from "@/server/lib/dataforseo/helpers";

const classifyKeywordsError = createDataforseoBillingClassifier({
  pathPrefix: "/keywords/",
  billingIssueCode: "KEYWORDS_BILLING_ISSUE",
  billingIssueMessage: "The connected DataForSEO account has a billing or balance issue",
});

async function fetchKeywordIdeas(projectId: string, kw: string) {
  const response = await keywordsDataApi(classifyKeywordsError).relatedKeywordsLive([
    { keyword: kw, location_code: 2840, language_code: "en" }
  ]);
  
  // Throws RATE_LIMITED AppError if DataForSEO returned 429
  const task = assertOk(response, {
    classify: classifyKeywordsError,
    classifyPath: "/v3/keywords_data/related_keywords/live"
  });

  // Billing metadata attached to task result
  const { data, billing } = parseTaskItems(task);
  return { data, billing };
}

Self-Hosted vs. Hosted Billing Modes

Aspect Hosted Mode Self-Hosted Mode
Credit pool Managed by OpenSEO Direct DataForSEO billing
Cost calculation Raw cost × 1.28 markup Raw cost only
Credit tracking Autumn events API N/A
Implementation check isHostedServerAuthMode() returns true isHostedServerAuthMode() returns false

The early-return logic in src/serverFunctions/billing.ts prevents unnecessary credit operations when running self-hosted.

Key Files Reference

File Purpose
src/shared/billing.ts Credit pools, conversion functions, markup logic
src/server/lib/dataforseo/core.ts Authentication, timeout, retries, error mapping
src/server/lib/dataforseoBillingClassification.ts Balance error classification
src/serverFunctions/billing.ts Autumn events fetching with backoff
src/server/mcp/tools/whoami.ts Credit balance query (no consumption)
src/server/lib/market.ts Location validation to prevent wasted credits

Summary

  • Authentication layer: createAuthenticatedFetch in core.ts centralizes all DataForSEO API calls with 60s timeout
  • Billing classification: Per-path error classifiers convert balance errors into typed AppError codes
  • Credit pools: Monthly + top-up pools with 1:1000 USD conversion and 1.28× hosted markup
  • Rate limiting: 429 responses map to RATE_LIMITED errors; Autumn API calls use exponential backoff
  • Retry policies: Idempotent reads retry twice with 250ms delay; non-idempotent calls never retry
  • Deployment modes: Hosted uses OpenSEO credit system; self-hosted pays DataForSEO directly

Frequently Asked Questions

What happens when DataForSEO returns HTTP 429?

OpenSEO maps the 429 status to a RATE_LIMITED AppError. The calling workflow determines whether to retry later or display a rate-limited message to users. For Autumn billing API calls, the client automatically retries with exponential backoff up to 3 times.

How does OpenSEO prevent double-charging for API calls?

Non-idempotent operations like Lighthouse live tests have retries disabled in createAuthenticatedFetch. Only idempotent read operations (keyword data, ranking checks) retry on transient 5xx errors, ensuring charged operations execute exactly once.

What is the credit conversion rate for DataForSEO services?

OpenSEO uses a fixed rate of 1000 DataForSEO credits per 1 USD. Hosted customers see approximately 1.28× this base cost after markup. Self-hosted users pay the raw DataForSEO rate directly without OpenSEO's credit pooling system.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →