How OpenSEO Handles Billing for the Hosted Version: Autumn Platform Integration
OpenSEO’s hosted version implements usage-based billing through the Autumn platform, applying a 28% markup to DataForSEO costs while maintaining separate credit pools for monthly allocations and top-ups, with all logic gated behind an environment detection check that distinguishes SaaS deployments from self-hosted instances.
The OpenSEO repository (every-app/open-seo) supports two distinct deployment models: self-hosted instances running on private infrastructure and the managed SaaS offering at openseo.so. The hosted version implements a comprehensive billing system that transforms raw DataForSEO API costs into customer charges through the Autumn billing platform, featuring automatic customer provisioning, subscription verification, and real-time usage credit tracking. Understanding how OpenSEO handles billing for the hosted version requires examining the runtime environment detection, the Autumn SDK integration, and the shared credit pool mechanics that differentiate SaaS pricing from self-hosted raw rates.
Runtime Environment Detection: Hosted vs. Self-Hosted
All billing logic depends on isHostedServerAuthMode() defined in src/server/lib/runtime-env.ts. When this function returns true, the request executes billing-aware code paths including customer creation and credit deduction. Self-hosted deployments return false here, skipping all Autumn-related charges entirely and allowing users to utilize their own DataForSEO API keys at raw cost.
import { isHostedServerAuthMode } from "@/server/lib/runtime-env";
if (await isHostedServerAuthMode()) {
// Execute billing logic: check credits, apply markup, track usage
} else {
// Self-hosted mode: use raw DataForSEO pricing without platform fees
}
Customer Lifecycle and Subscription Management
Provisioning Autumn Customers
When users first access paid features, getOrCreateOrganizationCustomer() in src/server/billing/subscription.ts creates an Autumn customer record tied to the organization ID. The function caches customer existence in Workers KV to minimize redundant API calls and ensure idempotent operations.
import { getOrCreateOrganizationCustomer } from "@/server/billing/subscription";
const billingCustomer = await getOrCreateOrganizationCustomer({
organizationId: ctx.organizationId,
userEmail: ctx.userEmail,
userId: ctx.userId,
});
Validating Plan Access
The system verifies entitlements through customerHasPaidPlan() and customerHasManagedAccess() in the same file. These functions call autumn.check() with feature identifiers AUTUMN_PAID_PLAN_FEATURE_ID and AUTUMN_MANAGED_ACCESS_FEATURE_ID to determine whether an organization may access managed-service features beyond the trial tier.
import { customerHasPaidPlan } from "@/server/billing/subscription";
const hasPlan = await customerHasPaidPlan(billingCustomer.id);
if (!hasPlan) {
// Trigger upgrade flow or restrict feature access
}
Usage Credit System and Cost Calculation
Shared Credit Pool Architecture
Hosted customers draw from a unified credit pool comprising a monthly balance (usage_credits) and an optional top-up balance (topup_credits). The function getUsageCreditsRemaining() retrieves available balances, while trackUsageCreditSpend() deducts costs atomically against this pool before executing costly operations like DataForSEO lookups or LLM inference.
import { trackUsageCreditSpend } from "@/server/billing/subscription";
await trackUsageCreditSpend({
customer: billingCustomer,
customerId: billingCustomer.id,
creditFeature: "seo_data_usage",
costUsd: rawDataForSeoCost,
monthlyRemaining: remainingCredits.monthlyRemaining,
});
The 28% Platform Markup
Unlike self-hosted users who pay DataForSEO directly, hosted customers incur a platform fee. The applyBillingMarkupUsd() function in src/shared/billing.ts implements this by multiplying raw USD amounts by SEO_DATA_COST_MARKUP (set to 1.28) and rounding via roundUsdForBilling().
import { applyBillingMarkupUsd } from "@/shared/billing";
const rawCost = 0.12; // DataForSEO API cost
const billedCost = applyBillingMarkupUsd(rawCost); // Returns 0.154 USD (28% markup)
Telemetry and Analytics Integration
After each credit deduction, trackUsageCreditSpend() emits PostHog events defined in src/server/lib/posthog.ts, including usage:credits_consume for successful transactions and usage:credits_gate_refused when the pool is depleted. These events feed the hosted UI with real-time usage statistics and trigger "Insufficient credits" alerts when balances approach zero.
If the credit pool is exhausted during a request, the system throws AppError("INSUFFICIENT_CREDITS"), blocking the operation and prompting the user to purchase additional top-up credits.
Retrieving Historical Billing Data
The server function getBillingUsageEvents() in src/serverFunctions/billing.ts queries the Autumn event log to display usage history in the hosted dashboard. This endpoint contains an additional isHostedServerAuthMode() guard to ensure self-hosted instances never attempt to fetch Autumn data or expose billing history endpoints.
Key Files in the Billing Pipeline
src/server/lib/runtime-env.ts– Determines deployment mode viaisHostedServerAuthMode(), gating all billing logicsrc/server/billing/subscription.ts– Core billing orchestration: customer creation, plan verification, and credit managementsrc/shared/billing.ts– Constants for Autumn feature IDs, markup calculations (applyBillingMarkupUsd), and rounding utilitiessrc/server/billing/autumn.ts– Lazy-initialized SDK wrapper providingcheck,track, andcustomers.getOrCreatemethodssrc/serverFunctions/billing.ts– API surface for historical billing event retrieval (hosted-only)src/server/lib/posthog.ts– Analytics telemetry for credit consumption and gating events
Summary
- OpenSEO uses the Autumn billing platform exclusively for its hosted SaaS deployment, while self-hosted instances bypass all billing logic through
isHostedServerAuthMode() - All DataForSEO costs incur a 28% platform markup via
applyBillingMarkupUsd()insrc/shared/billing.ts, implemented asSEO_DATA_COST_MARKUP = 1.28 - Usage operates on a shared credit pool combining monthly allocations and top-up balances, tracked through
getUsageCreditsRemaining()andtrackUsageCreditSpend()insrc/server/billing/subscription.ts - The system emits PostHog telemetry events for credit consumption and refusal, enabling real-time usage monitoring in the hosted UI
- Self-hosted users pay raw DataForSEO rates using their own API keys, while hosted customers manage billing entirely through Autumn with automatic customer provisioning and usage-based deductions
Frequently Asked Questions
How does OpenSEO distinguish between hosted and self-hosted billing?
OpenSEO checks isHostedServerAuthMode() in src/server/lib/runtime-env.ts at the start of every request. When this returns true, the code executes Autumn billing logic including credit checks and markup application. Self-hosted deployments return false, causing the system to skip all billing gates and use raw DataForSEO pricing with the user's own API credentials.
What percentage markup does the hosted version apply to DataForSEO costs?
The hosted version applies a 28% platform markup to all DataForSEO usage. The constant SEO_DATA_COST_MARKUP is set to 1.28 in src/shared/billing.ts, and the function applyBillingMarkupUsd() multiplies raw costs by this factor before deducting from the user's credit balance.
Can self-hosted users access the Autumn billing features or usage credit system?
No. Self-hosted instances cannot access Autumn billing features, usage credit tracking, or the markup calculation system. The getBillingUsageEvents() endpoint and all trackUsageCreditSpend() calls are gated behind isHostedServerAuthMode(), ensuring self-hosted users interact directly with DataForSEO and LLM providers using their own accounts and API keys.
What happens when a hosted customer exhausts their usage credits?
When the combined monthly and top-up credit balance reaches zero, trackUsageCreditSpend() emits a usage:credits_gate_refused PostHog event and throws AppError("INSUFFICIENT_CREDITS"). This blocks the current request and triggers the UI to display an "Insufficient credits" message, requiring the user to purchase additional credits before continuing to use paid features.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →