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:

  1. Credit pre-check: Calls assertUsageCreditsAvailable to verify the organization has sufficient balance.
  2. Execution: Runs the actual SDK method.
  3. Post-call tracking: Invokes trackDataforseoCost to 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
});
// 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, and aiSearch categories
  • Market-aware execution accepts locationCode and languageCode for 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:

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 →