# How OpenSEO Integrates with DataForSEO: A Code-Level Look at the Metered API Wrapper

> Discover how OpenSEO integrates with DataForSEO via a metered API wrapper. Explore the code for lazy loading, credit limits, and automatic billing tracking.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: internals
- Published: 2026-09-01

---

**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.

```ts
// 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.

```ts
// 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:

```ts
// 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.

```ts
// 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

```ts
// 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

```ts
// 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

```ts
// 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

```ts
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

```ts
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

```ts
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`](https://github.com/every-app/open-seo/blob/main/src/server/lib/dataforseo/client.ts) | Core metered client factory and billing wrapper |
| [`src/server/lib/dataforseo/sections.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/dataforseo/sections.ts) | Dynamic import target containing actual SDK fetchers |
| [`src/server/mcp/tools/dataforseo-research-tools.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/dataforseo-research-tools.ts) | MCP tool implementations using the client |
| [`src/server/workflows/RankCheckWorkflow.ts`](https://github.com/every-app/open-seo/blob/main/src/server/workflows/RankCheckWorkflow.ts) | Server workflow example with direct client usage |
| [`src/server/lib/billing/subscription.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/billing/subscription.ts) | Credit limit enforcement and spend recording |
| [`src/shared/billing-credit-features.ts`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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.