How OpenSEO Handles Billing and Credits for API Calls: A Complete Technical Guide

OpenSEO uses a dual-pool credit system powered by the Autumn subscription service, where organizations spend from monthly usage credits first, then top-up credits, with every API call tracked through trackUsageCreditSpend in src/server/billing/subscription.ts.

OpenSEO's billing infrastructure is tightly integrated with its API consumption model. Whether you're running DataForSEO requests or LLM-powered onboarding calls, every paid operation draws from a shared usage-credit pool. This guide explains exactly how the system manages customer accounts, feature flags, credit balances, and spend tracking based on the every-app/open-seo source code.


Autumn Customer Representation

Every organization using OpenSEO is represented as an Autumn customer. The organization ID serves directly as the Autumn customerId, creating a 1:1 mapping between your OpenSEO org and the billing system.

The helper getOrCreateOrganizationCustomer in src/server/billing/subscription.ts handles this lazily—creating the customer on first use and caching the existence flag for 24 hours to minimize API round-trips:

import { getOrCreateOrganizationCustomer } from "@/server/billing/subscription";

const { id: customerId } = await getOrCreateOrganizationCustomer({
  organizationId: orgId,
  userEmail: user.email,
  userId: user.id,
});
// Customer created only if not exists; cached for 24h

This pattern ensures that billing operations never fail due to missing customer records while keeping latency low for repeated calls.


Billing Feature Flags and Constants

All billing-related identifiers are centralized in src/shared/billing.ts. These constants define which Autumn features control access and credit pools:

Constant Purpose
AUTUMN_PAID_PLAN_FEATURE_ID Indicates active paid subscription
AUTUMM_MANAGED_ACCESS_FEATURE_ID Grants managed service access
AUTUMN_SEO_DATA_BALANCE_FEATURE_ID Monthly usage credits pool
AUTUMN_SEO_DATA_TOPUP_BALANCE_FEATURE_ID Purchased top-up credits pool

The file also defines the credit economics:

// src/shared/billing.ts
export const AUTUMN_SEO_DATA_CREDITS_PER_USD = 1000;  // 1000 credits = $1
export const SEO_DATA_COST_MARKUP = 1.28;              // 28% platform markup

The markup is applied to raw provider costs (like DataForSEO) before credit conversion, ensuring platform sustainability.


Credit Pool Architecture

OpenSEO maintains two distinct credit pools per organization:

  1. Monthly balance (usage_credits) – Refills automatically each billing cycle; primary spending source
  2. Top-up balance (topup_credits) – Purchased manually; acts as overflow when monthly credits deplete

This dual-pool design provides predictable baseline usage with flexible overflow protection.

Checking Credit Availability

Before any paid API call, the system verifies sufficient credits exist. The checkUsageCreditsDepleted function (lines 58–92 in src/server/billing/subscription.ts) implements defensive logic:

// Simplified flow from source
const check = await autumn.check({
  customerId: orgId,
  featureId: AUTUMN_SEO_DATA_BALANCE_FEATURE_ID,
});

const monthly = check?.qty || 0;
const topupCheck = await autumn.check({
  customerId: orgId, 
  featureId: AUTUMN_SEO_DATA_TOPUP_BALANCE_FEATURE_ID,
});
const topup = topupCheck?.qty || 0;

if (monthly + topup <= 0) {
  // Double-check with full customer read to guard against stale cache
  const fullCustomer = await autumn.customers.get(orgId);
  // ...verify depletion and emit usage:credits_gate_refused event
}

If the initial read shows zero or negative balance, a second full-customer read guards against transient read errors before blocking the operation.

For simpler cases, assertUsageCreditsAvailable (lines 10–21) provides a throwing wrapper:

import { assertUsageCreditsAvailable } from "@/server/billing/subscription";

await assertUsageCreditsAvailable(orgId); 
// Throws INSUFFICIENT_CREDITS if both pools are empty

Recording API Spend with trackUsageCreditSpend

When an API call completes, trackUsageCreditSpend handles the actual credit deduction. This is the core billing integration point called throughout the codebase.

The Spend Tracking Flow

  1. Apply markupapplyBillingMarkupUsd multiplies raw cost by SEO_DATA_COST_MARKUP
  2. Convert to credits – USD amount × AUTUMN_SEO_DATA_CREDITS_PER_USD
  3. Deduct from monthly first – Primary pool depletion
  4. Deduct remainder from top-up – Only if monthly exhausted
  5. Emit tracking events – PostHog usage:credits_consume for analytics
import { trackUsageCreditSpend } from "@/server/billing/subscription";
import { AUTUMN_SEO_DATA_BALANCE_FEATURE_ID } from "@/shared/billing";

await trackUsageCreditSpend({
  customer: { 
    organizationId: orgId, 
    userId: user.id, 
    userEmail: user.email 
  },
  customerId: orgId,
  creditFeature: AUTUMN_SEO_DATA_BALANCE_FEATURE_ID,
  costUsd: rawProviderCost,           // e.g., $0.05 from DataForSEO
  monthlyRemaining: monthlyBalance,   // From prior checkUsageCreditsDepleted
  properties: { 
    requestId: "abc-123",
    endpoint: "serp/google/organic" 
  },
});

The function automatically handles split deductions—charging monthly credits first, then top-up credits—making multiple autumn.track calls as needed.


Real-World Integration Points

DataForSEO Client

The DataForSEO integration in src/server/lib/dataforseo/client.ts (lines 8–12, 210–218) demonstrates production usage. After receiving API response costs, it:

  1. Receives BillingCustomerContext containing org/user info
  2. Calls trackUsageCreditSpend with actual provider charges
  3. Passes through request metadata for audit trails

Workflow Integrations

Site audit and rank-check workflows pass billing context through their execution phases:

Both receive the same BillingCustomerContext shape, ensuring consistent attribution across all API-consuming operations.


Summary

  • Autumn integration: Organizations map 1:1 to Autumn customers via getOrCreateOrganizationCustomer with 24-hour caching
  • Dual credit pools: Monthly usage credits spend first; top-up credits provide overflow protection
  • Defensive checking: checkUsageCreditsDepleted double-reads on depletion to prevent false blocks
  • Markup and conversion: 28% platform markup applied before converting USD to credits at 1000:1 ratio
  • Centralized spend tracking: trackUsageCreditSpend handles all deductions, split spending, and analytics emission

Frequently Asked Questions

How does OpenSEO prevent API calls when credits run out?

The assertUsageCreditsAvailable function throws INSUFFICIENT_CREDITS before any paid operation begins. For additional safety, checkUsageCreditsDepleted performs a second full-customer read if the initial balance check shows zero credits, protecting against stale cache reads that might incorrectly allow spending.

What happens when monthly credits are exhausted but top-up credits exist?

trackUsageCreditSpend automatically deducts from the monthly balance first, then charges the remainder against top-up credits. Both deductions trigger separate autumn.track calls with their respective feature IDs (AUTUMN_SEO_DATA_BALANCE_FEATURE_ID and AUTUMN_SEO_DATA_TOPUP_BALANCE_FEATURE_ID).

Where is the billing markup configured?

The 28% platform markup is defined as SEO_DATA_COST_MARKUP = 1.28 in src/shared/billing.ts. This constant is applied via applyBillingMarkupUsd before credit conversion, ensuring consistent pricing across all API providers.

How is billing context passed through workflows?

All server-side API consumers receive a BillingCustomerContext object containing organizationId, userId, and userEmail. This context flows from workflow triggers through to trackUsageCreditSpend calls, enabling per-organization accounting with full user attribution for analytics.

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 →