# How OpenSEO Integrates with the DataForSEO API and Handles Billing Credits

> Discover how OpenSEO seamlessly integrates with the DataForSEO API, automatically managing billing credits for every request. Learn more about efficient API usage.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: how-to-guide
- Published: 2026-08-07

---

**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`](https://github.com/every-app/open-seo/blob/main/src/server/lib/dataforseo/client.ts) (lines 30‑34). This function imports the **sections barrel** from [`src/server/lib/dataforseo/sections.ts`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/client.ts)). This function implements a conditional execution path based on the server's authentication mode:

1. **Direct execution** – If `isHostedServerAuthMode` returns false, the request proceeds immediately without billing checks (useful for self-hosted or development environments).

2. **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.billing` in the response

Notably, the handler accounts for **charged errors**. If the API returns a `DataforseoChargedTaskError` (defined in [`src/server/lib/dataforseo/envelope.ts`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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

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

```typescript
// 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 when `loadDataforseoSections()` is first invoked via the barrel at [`src/server/lib/dataforseo/sections.ts`](https://github.com/every-app/open-seo/blob/main/src/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 checks `isHostedServerAuthMode`, validates credits via `assertUsageCreditsAvailable`, and handles both successful responses and `DataforseoChargedTaskError` exceptions.
- **Feature mapping** translates raw API paths (e.g., `v3/dataforseo_labs/google/related_keywords/live`) into business features like `"keyword_research"` using `mapDataforseoPathToCreditFeature`.
- **Cost tracking** finalizes the loop by calling `trackUsageCreditSpend` to 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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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.