How Open-SEO Tracks API Usage with Billing Credits: A Technical Deep Dive

Open-SEO uses a credit-based billing system that maps every DataForSEO API request to a specific credit feature, deducts credits from an organization's pool, and persists usage telemetry for real-time quota enforcement.

This system is built into the every-app/open-seo repository and ties directly into the DataForSEO (DFS) API. Understanding how billing credits track API usage requires examining the full pipeline—from credit pool definitions to real-time enforcement checks.

The Credit Pool Architecture

Every organization in Open-SEO maintains a shared credit pool with two distinct sources:

  • Monthly usage credits — allocated automatically through the feature ID usage_credits
  • Top-up credits — purchased additionally through the feature ID topup_credits

These constants are defined in src/shared/billing.ts alongside the conversion helper autumnSeoDataCreditsToUsd. The pool structure enables flexible billing where base subscriptions and one-time purchases coexist.

// From src/shared/billing.ts
export const AUTUMN_SEO_DATA_BALANCE_FEATURE_ID = "usage_credits";
export const AUTUMN_SEO_DATA_TOPUP_BALANCE_FEATURE_ID = "topup_credits";

export function autumnSeoDataCreditsToUsd(credits: number): number {
  return credits / AUTUMN_SEO_DATA_CREDITS_PER_USD;
}

The total available balance is simply the sum of both pools, calculated as (resp.base ?? 0) + (resp.topup ?? 0) whenever the balance is queried.

Mapping API Endpoints to Credit Features

Before any DFS call executes, Open-SEO determines which product line should bear the cost. The path of the endpoint—v3/dataforseo_labs/google/related_keywords/live for example—is converted into a CreditFeature enum value.

This mapping lives in src/shared/billing-credit-features.ts within the mapDataforseoPathToCreditFeature function (lines 32–73). The function inspects path segments and returns standardized feature names like keyword_research, domain_overview, backlinks, or site_audit.

// Example mapping from src/shared/billing-credit-features.ts
import { mapDataforseoPathToCreditFeature } from "@/shared/billing-credit-features";

const feature = mapDataforseoPathToCreditFeature([
  "v3",
  "dataforseo_labs",
  "google",
  "related_keywords",
  "live",
]); // Returns: "keyword_research"

This guarantees that usage analytics roll up correctly in the Autumn billing dashboard and that pricing tiers can be applied per feature.

Injecting Billing Context Into API Requests

Every tool that communicates with DFS receives a BillingCustomerContext object. The helper createDataforseoClient(context.billing) injects three critical pieces of information into request headers:

  1. Organization ID
  2. Selected credit feature (from the mapping above)
  3. Current credit balance

The DFS service then handles the actual deduction. You can see this pattern in src/server/mcp/tools/whoami.ts at line 26, where the client is constructed before any API call.

// From src/server/mcp/tools/whoami.ts
export async function whoami(context: RequestContext) {
  const client = createDataforseoClient(context.billing);
  const resp = await client.get("/whoami");
  const creditsRemaining = (resp.base ?? 0) + (resp.topup ?? 0);
  return { creditsRemaining };
}

Note that /whoami is a special endpoint—it returns the balance without consuming credits, making it safe for frequent UI refreshes.

Persisting Usage Telemetry

After DFS returns a response, the system records the transaction in two fields defined in src/db/telemetry.schema.ts:

  • creditsCharged — how many credits the specific request consumed
  • creditsRemaining — the organization's new balance after deduction

This persistence is handled by the runTask wrapper in src/server/lib/dataforseoBillingClassification.ts. The telemetry serves dual purposes: it powers the "credits remaining" display in the UI, and it provides the ground truth for pre-flight quota checks.

The schema design ensures that every API interaction leaves an audit trail, which is essential for billing disputes and usage analytics.

Quota Enforcement Before Spending Credits

Open-SEO prevents overage charges through pre-flight estimation. Before any credit-spending operation—rank tracking, site audit, keyword research—the code calls an estimate_*_cost function.

These estimators convert the projected number of DFS calls into costCredits. The workflow aborts immediately if creditsRemaining < costCredits.

// From src/server/mcp/tools/estimate-rank-tracker-cost.ts
import { estimate_rank_tracker_cost } from "@/server/mcp/tools/estimate-rank-tracker-cost";

const estimate = await estimate_rank_tracker_cost({
  trackerId,
  billingCustomer: context.billing,
});

if (estimate.costCredits > context.billing.creditsRemaining) {
  throw new Error("Insufficient credits for rank check");
}

This pattern appears across all expensive tools. The estimate is also surfaced to users before confirmation, providing transparency into expected costs.

Executing Credit-Spending Operations

Once a user approves the estimated cost, the actual work proceeds with a maximum approved ceiling. The run_rank_tracker tool accepts a maxCostCredits parameter, ensuring that even if the DFS response structure changes unexpectedly, spending cannot exceed the pre-approved amount.

// Executing with a hard spending limit
import { run_rank_tracker } from "@/server/mcp/tools/run-rank-tracker";

await run_rank_tracker({
  trackerId,
  maxCostCredits: approvedCredits, // Derived from the user's confirmation
  billingCustomer: context.billing,
});

This two-phase approach—estimate then execute with a ceiling—prevents surprise charges while allowing flexible, usage-proportional pricing.

USD Conversion for Reporting

For pricing pages, invoices, and analytics dashboards, credits convert to dollars using the constant AUTUMN_SEO_DATA_CREDITS_PER_USD. The helper autumnSeoDataCreditsToUsd in src/shared/billing.ts performs this calculation.

This conversion is decoupled from the core tracking logic—the system operates entirely in credits internally, converting only at presentation boundaries. This design simplifies testing and prevents rounding errors from accumulating in the balance tracking.

Summary

  • Credit pools combine monthly allocations and top-ups, defined in src/shared/billing.ts
  • Endpoint mapping ensures every DFS call charges to the correct product line via mapDataforseoPathToCreditFeature
  • Client creation injects billing context through createDataforseoClient, letting DFS handle deduction
  • Telemetry persistence records creditsCharged and creditsRemaining for UI sync and auditing
  • Pre-flight estimation enforces hard quotas before expensive operations can begin
  • Spending ceilings cap execution costs at user-approved amounts

Frequently Asked Questions

How does Open-SEO prevent users from overspending their credits?

Before any credit-consuming operation, the system calls an estimate_*_cost function that calculates the projected costCredits. The workflow checks this against context.billing.creditsRemaining and throws an error if funds are insufficient. Additionally, execution functions accept a maxCostCredits parameter that hard-limits actual spending to the pre-approved amount.

What happens when a user purchases top-up credits?

Top-up credits are stored separately under the feature ID topup_credits defined in src/shared/billing.ts. When calculating the total available balance, the system sums (resp.base ?? 0) + (resp.topup ?? 0). Top-ups consume after monthly credits are depleted, extending usage without changing the core tracking mechanics.

Where can I find the complete list of credit feature mappings?

The authoritative mapping lives in src/shared/billing-credit-features.ts. The mapDataforseoPathToCreditFeature function (lines 32–73) contains the logic that converts DFS endpoint paths into CreditFeature enum values like keyword_research, backlinks, and site_audit.

How does the telemetry system handle failed or partial API calls?

The runTask wrapper in src/server/lib/dataforseoBillingClassification.ts classifies DFS errors as billing issues and records the outcome. Credits are only deducted for successful responses, and the creditsRemaining field in telemetry always reflects the post-transaction state, ensuring the UI stays synchronized with actual spend.

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 →