# What Triggers Low Credit Warnings in OpenSEO: A Technical Code Analysis

> Discover what triggers low credit warnings in OpenSEO. Learn how the prepareRankCheckKeywords function checks credit availability and prevents metered operations when insufficient.

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

---

**Low credit warnings in OpenSEO trigger when the `prepareRankCheckKeywords` function detects that your available credits (monthly plus top-up) are insufficient to cover the estimated cost of an upcoming metered operation, throwing an `INSUFFICIENT_CREDITS` error that aborts the workflow and updates the UI state.**

OpenSEO monitors your organization's credit balance before executing any billable task, such as rank-tracking checks or DataForSEO API calls. According to the `every-app/open-seo` source code, the system implements a defensive guardrail in [`src/server/workflows/RankCheckWorkflow.ts`](https://github.com/every-app/open-seo/blob/main/src/server/workflows/RankCheckWorkflow.ts) that calculates operation costs against your remaining credits via the Autumn billing service. When the balance falls short, OpenSEO aborts the operation and surfaces a low credit warning through multiple channels to prevent unexpected billing.

## The Primary Trigger: Credit Validation in RankCheckWorkflow

### Estimating Operation Costs

In [`src/server/workflows/RankCheckWorkflow.ts`](https://github.com/every-app/open-seo/blob/main/src/server/workflows/RankCheckWorkflow.ts), the **`prepareRankCheckKeywords`** function (lines 101-124) serves as the gatekeeper for all rank-tracking operations. Before initiating any check, the system calls **`estimateRankCheckCredits`** to calculate the exact credit cost required for the requested keywords. This cost estimation happens immediately before the system commits to consuming resources.

### Querying the Autumn Billing Service

When running in hosted server auth mode, the workflow simultaneously queries two distinct Autumn billing features to determine your available resource pool:
- **`AUTUMN_SEO_DATA_BALANCE_FEATURE_ID`** (your monthly allocation)
- **`AUTUMN_SEO_DATA_TOPUP_BALANCE_FEATURE_ID`** (purchased top-up credits)

The system aggregates these into a single `available` value representing your total spendable credits.

### The INSUFFICIENT_CREDITS Abort Mechanism

If the aggregated `available` credits are less than the `costCredits`, the system immediately throws an **`AppError`** with the code **`INSUFFICIENT_CREDITS`** and the message "Insufficient credits for rank check". This hard stop prevents the operation from proceeding and initiating billing for resources you cannot cover.

```typescript
// src/server/workflows/RankCheckWorkflow.ts
if (await isHostedServerAuthMode()) {
  const [monthlyCheck, topupCheck] = await Promise.all([
    autumn.check({ customerId, featureId: AUTUMN_SEO_DATA_BALANCE_FEATURE_ID }),
    autumn.check({ customerId, featureId: AUTUMN_SEO_DATA_TOPUP_BALANCE_FEATURE_ID }),
  ]);
  const available = (monthlyCheck.balance?.remaining ?? 0) + (topupCheck.balance?.remaining ?? 0);
  if (available < costCredits) {
    // ❗ Low‑credit warning → abort the rank check
    throw new AppError("INSUFFICIENT_CREDITS", "Insufficient credits for rank check");
  }
}

```

## How Failed Runs Update the UI State

When a rank check fails due to insufficient credits, the **`markRankCheckRunFailed`** function (lines 44-48) in the same workflow file captures the specific error code. It persists the failure reason to the database by calling `RankTrackingRepository.updateConfig` and setting the **`lastSkipReason`** field to **`"insufficient_credits"`**. The front-end UI monitors this database flag to display persistent low-credit notifications on the tracker dashboard.

```typescript
// src/server/workflows/RankCheckWorkflow.ts – handling the failure
const isInsufficientCredits =
  input.error instanceof AppError && input.error.code === "INSUFFICIENT_CREDITS";
if (isInsufficientCredits) {
  await RankTrackingRepository.updateConfig(input.configId, input.projectId, {
    lastSkipReason: "insufficient_credits",   // ← UI reads this flag
  });
}

```

## Real-Time Credit Monitoring via MCP Tools

Beyond workflow interruptions, OpenSEO provides real-time credit visibility through the cached **`whoami`** tool in [`src/server/mcp/tools/whoami.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/whoami.ts). This Model Context Protocol (MCP) endpoint returns **`creditsRemaining`**—the sum of monthly and top-up balances—to connected clients. When this value is `null` or below safe operational thresholds, the client-side interface renders a "low credit" banner, giving users immediate visual feedback before they initiate new operations.

```typescript
// src/server/mcp/tools/whoami.ts (cached)
export const whoami = defineTool({
  name: "whoami",
  description: "Confirms the connected OpenSEO account and current credit balance.",
  schema: z.object({
    creditsRemaining: z.number().nullable(),
  }),
  async run({ base, topup }) {
    const creditsRemaining = (base ?? 0) + (topup ?? 0);
    return {
      creditsRemaining,
      text: `Credits remaining: ${creditsRemaining != null ? creditsRemaining.toLocaleString() : "unknown"}`,
    };
  },
});

```

## Summary

- **Credit validation** occurs before every metered operation in `prepareRankCheckKeywords` within [`src/server/workflows/RankCheckWorkflow.ts`](https://github.com/every-app/open-seo/blob/main/src/server/workflows/RankCheckWorkflow.ts).
- The system **aggregates monthly and top-up balances** from the Autumn billing service to calculate total available credits.
- When `available < costCredits`, an **`INSUFFICIENT_CREDITS`** error aborts the operation and triggers the warning cascade.
- Failed runs set **`lastSkipReason: "insufficient_credits"`** in the database via `RankTrackingRepository.updateConfig`, which the UI reads to display persistent alerts.
- The **`whoami`** MCP tool in [`src/server/mcp/tools/whoami.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/whoami.ts) provides real-time `creditsRemaining` data for proactive client-side warning banners.

## Frequently Asked Questions

### What specific error code indicates low credits in OpenSEO?

OpenSEO uses the error code **`INSUFFICIENT_CREDITS`** within the `AppError` class. This code is thrown in [`src/server/workflows/RankCheckWorkflow.ts`](https://github.com/every-app/open-seo/blob/main/src/server/workflows/RankCheckWorkflow.ts) when credit estimates exceed available balances, and is later checked in `markRankCheckRunFailed` to set the appropriate UI flags in the database.

### Where in the codebase does OpenSEO check credit balances before running rank checks?

The validation occurs in the **`prepareRankCheckKeywords`** function inside [`src/server/workflows/RankCheckWorkflow.ts`](https://github.com/every-app/open-seo/blob/main/src/server/workflows/RankCheckWorkflow.ts) (lines 101-124). This function queries the Autumn billing service for both monthly and top-up credit balances before permitting the operation to proceed.

### How does the OpenSEO UI know when to display a low credit warning?

The UI receives warnings through two mechanisms: first, by reading the **`lastSkipReason`** field set to `"insufficient_credits"` in the tracker configuration when a run fails, and second, by checking the **`creditsRemaining`** value returned by the cached `whoami` tool in [`src/server/mcp/tools/whoami.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/tools/whoami.ts).

### Can OpenSEO operations proceed if only top-up credits remain but monthly credits are depleted?

Yes, OpenSEO aggregates both balance types. The code explicitly sums `(monthlyCheck.balance?.remaining ?? 0) + (topupCheck.balance?.remaining ?? 0)` to determine total available credits. As long as the combined total covers the operation cost, the check will proceed.