How OpenSEO Integrates with DataForSEO: A Code-Level Look at the Metered API Wrapper
OpenSEO integrates with DataForSEO through a thin, metered wrapper in src/server/lib/dataforseo that lazy-loads the SDK, enforces credit limits, and automatically tracks billing for every API call.
The every-app/open-seo repository structures its DataForSEO integration as a three-layer abstraction that shields the rest of the application from raw SDK complexity while injecting billing-aware metering at every entry point. This architecture keeps server startup fast, centralizes cost attribution, and enables seamless market-specific targeting across MCP tools and server workflows.
The Three-Layer Integration Architecture
Layer 1: Lazy Loading of the DataForSEO SDK
The DataForSEO SDK (approximately 3MB) is never imported at startup. Instead, loadDataforseoSections() returns a promise that dynamically imports the heavy dependency only when the first API call occurs.
// src/server/lib/dataforseo/client.ts lines 30-34
export function loadDataforseoSections(): Promise<DataforseoSections> {
return (sectionsPromise ??= import("@/server/lib/dataforseo/sections"));
}
This pattern prevents cold-start latency in serverless environments and keeps the initial bundle size minimal.
Layer 2: The Metered Client Factory
The createDataforseoClient function produces a wrapper object whose methods are instrumented with billing logic. Each category—business, backlinks, keywords, domain, serp, labs, lighthouse, and aiSearch—exposes metered versions of the underlying SDK operations.
// src/server/lib/dataforseo/client.ts lines 63-72
export function createDataforseoClient(customer: BillingCustomerContext) {
return {
business: { /* metered methods */ },
backlinks: { /* metered methods */ },
keywords: { /* metered methods */ },
domain: { /* metered methods */ },
serp: { /* metered methods */ },
labs: { /* metered methods */ },
lighthouse: { /* metered methods */ },
aiSearch: { /* metered methods */ },
};
}
The meter helper injects credit-feature routing and spend tracking:
// src/server/lib/dataforseo/client.ts lines 48-58
function meter<I, T>(customer, pick, defaultFeature?) {
return (input) => meterDataforseoCall(
customer,
async () => pick(await loadDataforseoSections())(input),
input.creditFeature ?? defaultFeature
);
}
Layer 3: Hosted-Mode Billing and Error Handling
When OpenSEO runs as a hosted SaaS platform, meterDataforseoCall performs three critical operations before executing any DataForSEO request:
- Credit pre-check: Calls
assertUsageCreditsAvailableto verify the organization has sufficient balance. - Execution: Runs the actual SDK method.
- Post-call tracking: Invokes
trackDataforseoCostto record spend using the feature mapped from the SDK path.
Charged task errors are still tracked as costs unless they represent validation-only failures. This ensures accurate billing attribution even when DataForSEO returns partial or error responses.
// src/server/lib/dataforseo/client.ts lines 54-78
async function meterDataforseoCall<T>(customer, execute, creditFeature?) {
// Credit check, execution, and cost tracking implementation
}
How Higher-Level Code Uses the DataForSEO Integration
Most OpenSEO functionality obtains a client via createDataforseoClient(context.billing) and calls the appropriate domain-specific method.
Ranked Keywords MCP Tool
// src/server/mcp/tools/dataforseo-research-tools.ts lines 800-806
const client = createDataforseoClient(context.billing);
const keywords = await client.domain.rankedKeywords({
target: target.hostname,
locationCode: market.locationCode,
languageCode: market.languageCode,
// additional filtering options
});
Local Business Search
// src/server/mcp/tools/dataforseo-research-tools.ts lines 822-828
const client = createDataforseoClient(context.billing);
const rows = await client.business.businessListings({
categories: ["pizza_restaurant"],
title: "Joe's Pizzeria",
locationCoordinate: "37.7749,-122.4194,5",
});
Server Workflow: Rank Check
// src/server/workflows/RankCheckWorkflow.ts lines 334-336
const client = createDataforseoClient(billing);
const serp = await client.serp.rankCheck({
target: target.hostname,
locationCode: project.locationCode,
languageCode: project.languageCode,
});
Complete Integration Code Examples
Pulling Ranked Keywords for a Domain
import { createDataforseoClient } from "@/server/lib/dataforseo";
import { resolveMarketSelector } from "@/server/mcp/tools/dataforseo-research-tools";
const client = createDataforseoClient(billingContext);
const market = resolveMarketSelector(
{ locationCode: 284, languageCode: "en" }, // US-English
projectMeta
);
const result = await client.domain.rankedKeywords({
target: "example.com",
locationCode: market.locationCode,
languageCode: market.languageCode,
limit: 20,
orderBy: ["ranked_serp_element.serp_item.rank_absolute,asc"],
});
console.log(result.items); // Array of ranked keyword rows
Searching Local Businesses with Geographic Targeting
import { createDataforseoClient } from "@/server/lib/dataforseo";
const client = createDataforseoClient(billingContext);
const rows = await client.business.businessListings({
categories: ["pizza_restaurant"],
title: "Joe's Pizzeria",
locationCoordinate: "37.7749,-122.4194,5", // lat, lon, km radius
isClaimed: false,
limit: 10,
});
console.log(rows.map(r => r.title));
Direct Workflow Integration
import { createDataforseoClient } from "@/server/lib/dataforseo";
import { parseResearchTargetOrThrow } from "@/server/lib/domainUtils";
export async function runRankCheck(project, billing, url) {
const client = createDataforseoClient(billing);
const target = parseResearchTargetOrThrow(url);
const serp = await client.serp.rankCheck({
target: target.hostname,
locationCode: project.locationCode,
languageCode: project.languageCode,
});
return serp; // Raw DataForSEO rank-check response
}
Key Source Files in the DataForSEO Integration
| File | Purpose |
|---|---|
src/server/lib/dataforseo/client.ts |
Core metered client factory and billing wrapper |
src/server/lib/dataforseo/sections.ts |
Dynamic import target containing actual SDK fetchers |
src/server/mcp/tools/dataforseo-research-tools.ts |
MCP tool implementations using the client |
src/server/workflows/RankCheckWorkflow.ts |
Server workflow example with direct client usage |
src/server/lib/billing/subscription.ts |
Credit limit enforcement and spend recording |
src/shared/billing-credit-features.ts |
SDK path to billing feature mapping |
Summary
The OpenSEO DataForSEO integration provides a unified, billing-aware façade with these characteristics:
- Lazy loading eliminates startup overhead for the 3MB SDK
- Automatic metering enforces credit limits and tracks spend per feature in hosted deployments
- Unified client interface exposes
business,domain,serp,keywords,labs,backlinks,lighthouse, andaiSearchcategories - Market-aware execution accepts
locationCodeandlanguageCodefor geographic targeting - Error-resilient billing still records costs for charged task errors unless validation-only
Frequently Asked Questions
What is the performance impact of the DataForSEO SDK on OpenSEO startup time?
There is no startup impact. The SDK is lazy-loaded via dynamic import() only when the first API call executes. The loadDataforseoSections function caches the promise, ensuring subsequent calls reuse the same loaded module without re-importing.
How does OpenSEO track costs for different DataForSEO features?
Costs are mapped through mapDataforseoPathToCreditFeature in src/shared/billing-credit-features.ts. Each metered method specifies a default credit feature (e.g., rank_tracking, local_seo), which can be overridden via the creditFeature input parameter. The meterDataforseoCall function then records spend against that feature after successful execution.
Can I use the DataForSEO client without billing enforcement?
In self-hosted OpenSEO deployments, the billing context may be null or bypassed. The meterDataforseoCall function checks for hosted-mode availability before enforcing credit limits. When billing infrastructure is absent, calls execute directly without pre-checks or spend tracking, though the lazy-loading wrapper remains active.
What markets (locations and languages) does the integration support?
Any market supported by DataForSEO. The client accepts standard locationCode and languageCode parameters on every method. OpenSEO provides resolveMarketSelector utilities to merge explicit market selections with project defaults, enabling flexible geographic targeting across 100+ countries and languages.
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 →