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

> Discover how Open-SEO tracks API usage with billing credits. Learn how credit deduction and usage telemetry ensure real-time quota enforcement for your organization.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: deep-dive
- Published: 2026-08-18

---

**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`](https://github.com/every-app/open-seo/blob/main/src/shared/billing.ts) alongside the conversion helper `autumnSeoDataCreditsToUsd`. The pool structure enables flexible billing where base subscriptions and one-time purchases coexist.

```typescript
// 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`](https://github.com/every-app/open-seo/blob/main/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`.

```typescript
// 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`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/whoami.ts) at line 26, where the client is constructed before any API call.

```typescript
// 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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`.

```typescript
// 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.

```typescript
// 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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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.