How OpenSEO Integrates with the DataForSEO API and Handles Billing Credits
OpenSEO integrates with the DataForSEO API through a lazy-loaded client that meters every request and automatically deducts costs from organization billing credits.
The every-app/open-seo repository implements a fully-instrumented bridge to the DataForSEO platform, ensuring that SEO data retrieval is both performant and cost-transparent. By dynamically importing the heavy SDK only when needed and wrapping every endpoint in billing-aware middleware, the system guarantees that organizations never exceed their allocated usage credits. This architecture separates the concerns of API communication, cost accounting, and credit management into distinct, testable layers.
Lazy Loading the DataForSEO SDK
To minimize server startup overhead, OpenSEO avoids bundling the dataforseo-client (~3 MB) in the main isolate. Instead, the system employs a dynamic import strategy that pulls in the SDK only upon first use.
The entry point is loadDataforseoSections() in src/server/lib/dataforseo/client.ts (lines 30‑34). This function imports the sections barrel from src/server/lib/dataforseo/sections.ts, which re-exports all section fetchers and triggers the heavy dependency load. Once imported, the sections are cached for subsequent requests, ensuring that the initial latency penalty is paid only once per server instance.
This lazy-loading pattern keeps the base server footprint small while still providing full access to DataForSEO's comprehensive SEO data endpoints.
Creating a Metered Client
The factory function createDataforseoClient(customer) (lines 63‑100 in client.ts) returns a proxy-like object whose nested properties—such as serp.live and keywords.related—are automatically instrumented. Each property is wrapped by the internal meter helper, which receives:
- The billing customer context (organization identity)
- A picker function that selects the specific fetcher from the lazy-loaded sections
- An optional default credit feature for categorization
The meter function constructs an async wrapper that intercepts every API call. When invoked, it executes meterDataforseoCall, passing the customer context, the actual API operation, and the credit feature identifier. This ensures that billing logic remains transparent to the business code consuming the client.
Metering Logic and Credit Checks
The core enforcement logic resides in meterDataforseoCall (lines 136‑188 in client.ts). This function implements a conditional execution path based on the server's authentication mode:
-
Direct execution – If
isHostedServerAuthModereturns false, the request proceeds immediately without billing checks (useful for self-hosted or development environments). -
Hosted mode enforcement – In production, the function:
- Retrieves or creates the billing record via
getOrCreateOrganizationCustomer - Validates available funds through
assertUsageCreditsAvailable - Executes the DataForSEO API call
- Captures billing metadata from
result.billingin the response
- Retrieves or creates the billing record via
Notably, the handler accounts for charged errors. If the API returns a DataforseoChargedTaskError (defined in src/server/lib/dataforseo/envelope.ts), indicating a malformed request that still incurred cost, the system still records the spend via trackDataforseoCost before propagating the error to the caller.
Mapping API Paths to Credit Features
Before costs can be recorded, OpenSEO must categorize the request. The helper mapDataforseoPathToCreditFeature (lines 27‑71 in src/shared/billing-credit-features.ts) translates raw API paths into symbolic product features.
For example, the path ["v3","dataforseo_labs","google","related_keywords","live"] maps to "keyword_research", while SERP rank-checking endpoints map to "rank_tracking". This mapping drives both the analytics dashboard and the credit deduction logic, allowing organizations to see exactly which SEO capabilities consume their quota.
Recording Usage and Deducting Credits
Once the API call completes and the feature is identified, trackDataforseoCost (lines 190‑210 in client.ts) forwards the cost (in USD), the resolved credit feature, and metadata (provider, API path, cache status) to the subscription service. The function trackUsageCreditSpend then subtracts the amount from the organization's monthly credit pool and updates real-time UI dashboards.
This creates a closed loop: every DataForSEO request is tagged, costed, and accounted for against the specific organization's billing record.
Code Examples
Creating a Metered Client
import { createDataforseoClient } from '@/server/lib/dataforseo';
import { getCurrentCustomerContext } from '@/server/billing/subscription';
const customer = await getCurrentCustomerContext();
const dfseo = createDataforseoClient(customer);
// Request live SERP results – credit use is deducted automatically
const serpResult = await dfseo.serp.live({
creditFeature: 'keyword_research', // optional override
// …SERP request payload
});
// Request rank-check SERP – defaults to the "rank_tracking" feature
const rankCheck = await dfseo.serp.rankCheck({
// …payload
});
Meter Wrapper Internals
// Simplified view of the instrumentation layer
function meter<I, T>(customer, pick, defaultFeature?) {
return async (input) => {
const result = await meterDataforseoCall(
customer,
async () => pick(await loadDataforseoSections())(input),
input.creditFeature ?? defaultFeature,
);
return result;
};
}
Summary
- Lazy loading keeps the
dataforseo-client(~3 MB) out of the initial server bundle, importing it only whenloadDataforseoSections()is first invoked via the barrel atsrc/server/lib/dataforseo/sections.ts. - Metered clients created by
createDataforseoClient()wrap every API section in a billing-aware proxy that intercepts calls before they reach the DataForSEO API. - Credit enforcement occurs in
meterDataforseoCall, which checksisHostedServerAuthMode, validates credits viaassertUsageCreditsAvailable, and handles both successful responses andDataforseoChargedTaskErrorexceptions. - Feature mapping translates raw API paths (e.g.,
v3/dataforseo_labs/google/related_keywords/live) into business features like"keyword_research"usingmapDataforseoPathToCreditFeature. - Cost tracking finalizes the loop by calling
trackUsageCreditSpendto deduct the exact USD amount from the organization's credit balance.
Frequently Asked Questions
How does OpenSEO prevent the DataForSEO SDK from bloating the server startup?
OpenSEO uses dynamic imports via loadDataforseoSections() to pull in the dataforseo-client only when the first DataForSEO endpoint is accessed. The SDK is imported through a barrel file at src/server/lib/dataforseo/sections.ts, ensuring the ~3 MB dependency loads on-demand rather than at server initialization.
What happens if an organization runs out of billing credits during a request?
Before executing any DataForSEO API call in hosted mode, meterDataforseoCall invokes assertUsageCreditsAvailable to verify sufficient funds. If credits are exhausted, the function throws an error before the external request is made, preventing negative balances and failed charge attempts.
How does the system handle API errors that still incur charges?
The envelope type DataforseoChargedTaskError (defined in src/server/lib/dataforseo/envelope.ts) captures malformed requests that DataForSEO charges for despite failing. The metering logic detects this error type in meterDataforseoCall and still invokes trackDataforseoCost to record the spend before returning the error to the caller.
Can developers override the default credit feature for specific API calls?
Yes. When calling any metered method, developers can pass a creditFeature property in the input object to override the default mapping. For example, passing creditFeature: 'custom_feature' to dfseo.serp.live() forces the cost to be recorded against that specific feature category rather than the default "keyword_research" or "rank_tracking" classification.
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 →