# Credits

Source: https://docs.recoupable.dev/credits

How Recoup credits work — what's billed, how to check your balance, and how to upgrade.

Some Recoup endpoints are billed in **credits** — primarily endpoints that hit external data providers, run AI inference, or generate content. Check the specific endpoint contract for other charges and subscription gates.

---

## What's billed

| Family | Billed? | Notes |
| --- | --- | --- |
| **Research** ([`/api/research/*`](https://docs.recoupable.dev/api-reference/research/web)) | Yes | Each successful call deducts credits. Costs vary by endpoint and parameters (e.g. [`enrich`](https://docs.recoupable.dev/api-reference/research/enrich) charges by processor tier; [`extract`](https://docs.recoupable.dev/api-reference/research/extract) charges by URL count). |
| **Content generation** ([`/api/content/image`](https://docs.recoupable.dev/api-reference/content/generate-image), [`video`](https://docs.recoupable.dev/api-reference/content/generate-video), upscale, transcribe and analyze) | Yes | Model-specific generation and inference charges apply. Hosted pipelines charge their component operations; estimates are advisory. |
| **AI Chat — streaming** ([`POST /api/chat`](https://docs.recoupable.dev/api-reference/chat/workflow)) | Yes | Variable cost based on model token usage, with a per-request minimum. |
| **Music analysis** ([`POST /api/songs/analyze`](https://docs.recoupable.dev/api-reference/songs/analyze)) | Yes | $0.05 per model request plus $0.001166 per second of inference time; `full_report` is one batched request for all 13 sections. |
| **Music analysis batch** ([`POST /api/songs/analyze/batch`](https://docs.recoupable.dev/api-reference/songs/analyze-batch)) | Yes | $0.05 once per batch plus $0.001166 per second of the batch's inference time, however many prompts it carries. |
| **Song moments** ([`POST /api/songs/moments`](https://docs.recoupable.dev/api-reference/songs/moments)) | Yes | $0.01 per song measured, plus the music analysis rate for each moment described. |
| **Social scrape** ([`/api/socials/{id}/scrape`](https://docs.recoupable.dev/api-reference/social/scrape), [`/api/artist/socials/scrape`](https://docs.recoupable.dev/api-reference/artist/socials-scrape)) | Yes | $0.05, plus $0.01 per post requested via `posts` — per social profile scraped. |
| **Other operations** | Consult the endpoint contract | [Artist CRUD](https://docs.recoupable.dev/api-reference/artists/list), [sandboxes](https://docs.recoupable.dev/api-reference/sandboxes/list), [sessions](https://docs.recoupable.dev/api-reference/sessions/get), [scheduled tasks](https://docs.recoupable.dev/api-reference/tasks/get), [account/org management](https://docs.recoupable.dev/api-reference/accounts/id), [agent signup](https://docs.recoupable.dev/api-reference/agents/signup), [Spotify proxies](https://docs.recoupable.dev/api-reference/spotify/search), etc. Subscription gating may still apply. |

Upstream generation or inference may incur cost even if a later composition or delivery step fails. Do not infer zero spend from the final pipeline status; inspect usage events. Credit gates reject before provider work when the balance is insufficient.

---

## What a credit is worth

**Balances and prices are US dollars.** The ledger stores amounts as integer micro-dollars, the same 6-decimal unit as USDC: `1,000,000 = $1.00`, so it can carry sub-cent charges (a $0.002/s provider rate prices exactly). Every credit field the API returns is that integer; divide by 1,000,000 to display it as currency. Example: [`GET /api/accounts/{id}/credits`](https://docs.recoupable.dev/api-reference/accounts/credits-get).

---

## Check your balance

```bash
curl -sS https://api.recoupable.dev/api/accounts/$ACCOUNT_ID/credits \
  -H "x-api-key: $RECOUP_API_KEY"
```

Response shape:

```json
{
  "account_id": "11111111-1111-4111-8111-111111111111",
  "remaining_credits": 292450000,
  "total_credits": 300000000,
  "used_credits": 7550000,
  "is_pro": true,
  "plan": "pro",
  "task_limit": null,
  "min_cadence_minutes": 60,
  "timestamp": "2026-10-01T12:00:00Z"
}
```

`plan`, `task_limit`, and `min_cadence_minutes` describe the [plan](https://docs.recoupable.dev/credits#subscription) the balance belongs to. `total_credits` is your plan-derived monthly allotment. `remaining_credits` can exceed `total_credits` after a top-up or an admin grant, and `used_credits` clamps to 0 in that case. Full schema at [Get Account Credits](https://docs.recoupable.dev/api-reference/accounts/credits-get).

### Plan response examples

`is_pro` on credits and `isPro` on subscriptions mean effective Pro, so both are
false for paid Starter. Use `plan` to distinguish Starter from Free. The credits
response reports the effective allowance; the [subscription response](https://docs.recoupable.dev/api-reference/accounts/subscription-get)
separately identifies whether it comes from the account or an organization.

| Situation | Credits `plan` / `is_pro` | Subscription `source` | Task limit / minimum cadence |
| --- | --- | --- | --- |
| Free | `free` / `false` | `null` | 1 / weekly |
| Paid Starter | `starter` / `false` | `account` | 3 / daily |
| Direct Pro | `pro` / `true` | `account` | `null` (unlimited) / hourly |
| Starter account with organization Pro | `pro` / `true` | `organization` | `null` (unlimited) / hourly |
| Inactive subscription without another paid entitlement | `free` / `false` | `null` | 1 / weekly |

For example, a Pro balance of 325,000,000 against an allowance of 300,000,000
returns `used_credits: 0`. The API preserves a signed balance: a negative balance
can make this display exceed the allowance. This is not a ledger of charges.
A null timestamp means the timestamp is unavailable, not proof of no prior activity.
An inactive subscription resolves to `status: "none"` and null subscription/billing
fields; this endpoint does not return the canceled subscription's history. Billing
details may also be unavailable on an active response. Named reference examples
illustrate these states without quoting an available offer or a live payment.

To see what consumed the balance, list the charges line by line with [`GET /api/accounts/{id}/usage`](https://docs.recoupable.dev/api-reference/accounts/usage-get): one item per deduction, newest first, each with the amount in micro-dollars and as a dollar string, plus the total for the period. API charges name the endpoint that billed them in `model_id` (for example `POST /api/artist/socials/scrape`), so a list of charges reads as a list of calls.

---

## Subscription

For Platform, Advisory, Build + Partner, and Enterprise options, see [Recoup pricing](https://recoupable.dev/pricing). API and MCP usage is billed separately through credits.

Your account's current credit allowance and scheduled-task limits are returned by [Get Account Credits](https://docs.recoupable.dev/api-reference/accounts/credits-get). Use `total_credits`, `task_limit`, and `min_cadence_minutes` from that response rather than inferring limits from a public plan name.

The API may return account identifiers such as `free`, `starter`, or `pro`. These are documented API values; they are not the names of consulting engagements. The request and response schemas define the values accepted by each endpoint.

### Task limits

[`POST /api/tasks`](https://docs.recoupable.dev/api-reference/tasks/create) and [`PATCH /api/tasks`](https://docs.recoupable.dev/api-reference/tasks/update) [check the plan](https://docs.recoupable.dev/api-reference/accounts/credits-get) before writing. Creating a task past `task_limit`, re-enabling a disabled task past it, or saving a cron whose consecutive runs are closer together than `min_cadence_minutes` returns **HTTP 402** with `error: "plan_limit"`:

```json
{
  "status": "error",
  "error": "plan_limit",
  "limit": "task_count",
  "message": "Free includes 1 task. Starter includes 3, Pro is unlimited.",
  "plan": "free",
  "task_limit": 1,
  "min_cadence_minutes": 10080,
  "current_task_count": 1,
  "billingUrl": "https://app.recoupable.dev/plan"
}
```

`limit` is `task_count` or `min_cadence`; `current_task_count` excludes the task being created or updated. Edits that only touch `title`, `prompt`, or `model` never hit the gate.

### Monthly refill

The refill is a **floor, not an assignment.** It raises `remaining_credits` up to your plan's monthly total and never lowers it. A balance already above the plan total, whether from a [top-up](https://docs.recoupable.dev/credits#one-time-top-ups) or an admin grant, is left exactly as it is, so credits you bought or were granted are never taken away by the calendar.

The refill is lazy rather than scheduled: it applies on the next read of [`GET /api/accounts/{id}/credits`](https://docs.recoupable.dev/api-reference/accounts/credits-get) once your credits row is more than a month old. The balance you read is always the refilled one.

### Manage your subscription

Sign in to your [account billing page](https://app.recoupable.dev/plan) to review your subscription, price, and current terms. For programmatic checkout, use the accepted request values in [Create Subscription Session](https://docs.recoupable.dev/api-reference/subscriptions/sessions-create) and open its returned checkout URL. Review the price and billing terms before completing checkout.

Advisory and custom-build engagements begin with a conversation about scope. Their website plan selection does not create an API subscription or complete a payment.

### Check your tier

```bash
curl -sS https://api.recoupable.dev/api/accounts/$ACCOUNT_ID/subscription \
  -H "x-api-key: $RECOUP_API_KEY"
```

Get `$ACCOUNT_ID` from [`GET /api/accounts/id`](https://docs.recoupable.dev/api-reference/accounts/id) if you don't already have it. Response includes `isPro` (boolean), `status`, `plan`, and `source` (whether the subscription comes from the account itself or an organization the account belongs to). Full schema at [Get Subscription](https://docs.recoupable.dev/api-reference/accounts/subscription-get).

### One-time top-ups

You can purchase credits any time via [`POST /api/credits/sessions`](https://docs.recoupable.dev/api-reference/credits/sessions-create). The endpoint adapts to what's on the account:

- **Card on file → silent auto-charge.** Recoup charges your saved Stripe card off-session and returns `paymentIntentId`, `creditsPurchased`, and `totalCents`. Credits land within seconds.
- **No card, or Stripe declines the saved card → Stripe Checkout fallback.** The response contains a Checkout `url` you open in the browser. When Stripe specifically declined a saved card, the response also includes a `declineReason` (e.g. `insufficient_funds`, `expired_card`) so you can explain *why* before sending the customer to update billing.

```bash
curl -sS -X POST https://api.recoupable.dev/api/credits/sessions \
  -H "x-api-key: $RECOUP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"credits": 1000000, "successUrl": "https://chat.recoupable.dev/credits/success"}'
```

Full request/response schema at [Create Credits Top-Up Session](https://docs.recoupable.dev/api-reference/credits/sessions-create).

### Check which card will be charged

Before triggering a silent off-session charge, inspect the default payment method on file:

```bash
curl -sS https://api.recoupable.dev/api/accounts/$ACCOUNT_ID/payment-method \
  -H "x-api-key: $RECOUP_API_KEY"
```

Response shape:

```json
{
  "account_id": "11111111-1111-4111-8111-111111111111",
  "card": {
    "brand": "visa",
    "last4": "4242",
    "exp_month": 12,
    "exp_year": 2026,
    "funding": "credit"
  }
}
```

`card` is `null` when this lookup finds no supported default card, including when no tagged Stripe customer exists. It does not establish whether a card was saved previously or whether other saved cards remain. Removing one selected card can leave others available; read the current card and auto-top-up settings before deciding how to proceed. Expired cards are still returned (with their original `exp_month` / `exp_year`); callers should compare against the current date and warn the customer, since an off-session charge against an expired card will decline. Full schema at [Get Default Payment Method](https://docs.recoupable.dev/api-reference/accounts/payment-method-get).

---

## Running out of credits

Every billed API request runs a credit gate before it executes. If `remaining_credits` doesn't cover the request's cost, the request stops there and returns **HTTP 402**. Nothing is charged and no Stripe object is created.

The decision tree:

1. **Enough credits?** → Request proceeds, credits deducted on success.
2. **Short?** → Request returns **HTTP 402** with the balance, the cost, and a link to billing.

A card on the account is never charged on its own. Charging happens only when the account asks to buy credits, through [`POST /api/credits/sessions`](https://docs.recoupable.dev/api-reference/credits/sessions-create), or when the account has turned on auto top-up. Saving a card ahead of time via [`POST /api/accounts/{id}/payment-method`](https://docs.recoupable.dev/api-reference/accounts/payment-method-create) makes a one-time purchase a single call instead of a browser round-trip; it does not authorize a charge by itself. Your plan, card, and every payment are on [`/api/accounts/{id}/subscription`](https://docs.recoupable.dev/api-reference/accounts/subscription-get), [`/api/accounts/{id}/payment-method`](https://docs.recoupable.dev/api-reference/accounts/payment-method-get), and [`/api/accounts/{id}/payments`](https://docs.recoupable.dev/api-reference/accounts/payments-get); pass an organization id as `{id}` to read the organization's billing.

### Auto top-up (opt-in)

Auto top-up is off for every account until it is turned on with [`PUT /api/accounts/{id}/auto-top-up`](https://docs.recoupable.dev/api-reference/accounts/auto-top-up-update), which needs three things chosen by the account: `enabled`, the `amountCents` to buy each time (5.00 to 1,000.00 USD), and the `thresholdCents` balance that triggers it. Both are USD cents; the `remaining_credits` balance above is in credit micro-dollars, so divide it by 10,000 to compare. Turning it on requires a card on file.

Once on, a credit deduction classified as `web` can trigger a purchase when the balance falls below the threshold. API-sourced deductions, including plugin and API-triggered background work, spend existing credits and do not trigger auto-top-up, even when the account has enabled it. This does not change the account's saved settings. Eligible top-ups charge the card for the amount, grant credits, record a usage event and email a receipt. Guardrails: at most one top-up per account per 10 minutes; a card decline turns auto top-up off, records the decline message as `lastError` (returned by [`GET /api/accounts/{id}/auto-top-up`](https://docs.recoupable.dev/api-reference/accounts/auto-top-up-get)), and emails the account instead of retrying; a successful selected-card removal turns it off before detachment, even when other cards remain. Auto top-up never creates a checkout session and never touches an invoiced (enterprise) plan. Saving a card does not turn auto top-up on by itself.

### Remove a saved card

[`DELETE /api/accounts/{id}/payment-method`](https://docs.recoupable.dev/api-reference/accounts/payment-method-delete) turns off automatic credit purchases, then removes the selected saved card. It removes one card, preserving other cards, balances, and configured amounts and thresholds. It does not cancel subscriptions or invoices. Other saved cards can still appear in a later card read and fund a purchase you explicitly request; removal does not silently enable automatic purchases again.

Success returns **204** with no body. If no customer or selected card exists, **404** leaves settings and cards unchanged. A **500** can mean the settings save failed before card removal, or that settings were saved but detachment failed; in the latter case automatic purchases stay off. Read the current card and auto-top-up settings before retrying an error or uncertain response. Repeating DELETE after a completed removal may select and remove another card.

This operation does not cancel a charge already in progress or prevent someone from explicitly changing the settings concurrently.

---

## 402 Payment Required

When the gate comes up short, billed endpoints return **HTTP 402** with a unified body:

```json
{
  "error": "insufficient_credits",
  "remaining_credits": 120000,
  "required_credits": 1000000,
  "billingUrl": "https://app.recoupable.dev"
}
```

| Field | When it appears | Meaning |
| --- | --- | --- |
| `error` | Always | Literally `"insufficient_credits"` |
| `remaining_credits` | Always | Account balance at the moment of the failed request |
| `required_credits` | Always | What this endpoint needed |
| `billingUrl` | Always | Static link to the Recoup app, where a human can save a card and buy credits |

`billingUrl` is a constant, not a freshly minted Stripe Checkout Session. Retrying a credit-gated endpoint returns the same URL every time and creates nothing, so an unattended client that keeps hitting the gate is safe to leave running.

**How to react:**

- **Browser-driven UI** (e.g., the Recoup chat app): send the customer to `billingUrl`, or call [`POST /api/credits/sessions`](https://docs.recoupable.dev/api-reference/credits/sessions-create) inline and open the `url` it returns.
- **Direct REST client outside the plugin**: report `remaining_credits` and `required_credits` and handle `billingUrl` according to that client's billing interface. Do not treat a 402 as retryable.
- **Recoup plugin / MCP workflow**: preserve the task and explain the missing entitlement. Do not surface billing or checkout links, advertise plans, start subscriptions or credit purchases, or use another connector to bypass the limit. Use only existing entitlements; resume only after access is independently available and the user asks to continue.

---

## Cost per endpoint

Current as of this revision of the page. The authoritative source is the per-endpoint reference docs and the `required_credits` field on any 402 response.

| Endpoint | Cost |
| --- | --- |
| [`POST /api/chat`](https://docs.recoupable.dev/api-reference/chat/workflow) (streaming) | Variable, priced from the model's token usage; see `required_credits` on a 402 |
| [Recoup artist and track research](https://docs.recoupable.dev/api-reference/research/search) | $0.05 per successful research call (50,000 credits); track-name resolution can add separately charged provider lookups |
| [`POST /api/research/people`](https://docs.recoupable.dev/api-reference/research/people) | $0.05 per call |
| [`POST /api/research/web`](https://docs.recoupable.dev/api-reference/research/web) | $0.01 per call |
| [`POST /api/research/extract`](https://docs.recoupable.dev/api-reference/research/extract) | $0.05 × number of URLs |
| [`POST /api/research/enrich`](https://docs.recoupable.dev/api-reference/research/enrich) | $0.05 / $0.10 / $0.25 (base / core / ultra processor) |
| [`POST /api/research/deep`](https://docs.recoupable.dev/api-reference/research/deep) | $0.25 per call |
| [`POST /api/image/generate`](https://docs.recoupable.dev/api-reference/image/generation) | Per-call price; see endpoint reference |
| [`POST /api/songs/analyze`](https://docs.recoupable.dev/api-reference/songs/analyze) | `$0.05 + $0.001166 × elapsed_seconds` per model request; `full_report` is one request |
| [`POST /api/songs/analyze/batch`](https://docs.recoupable.dev/api-reference/songs/analyze-batch) | `$0.05 + $0.001166 × elapsed_seconds` once per batch, `elapsed_seconds` being the whole batch |
| [`POST /api/songs/moments`](https://docs.recoupable.dev/api-reference/songs/moments) | `$0.01` per song measured, plus `$0.05 + $0.001166 × elapsed_seconds` once for the moments described |

## Billing recovery and units

The REST `insufficient_credits` body retains `billingUrl`, a static app link, not a Stripe Checkout session. Plugin workflows must not surface that transactional recovery link or initiate a charge. The release-pack MCP adapter omits it from errors. The historical measurement gate returns `payment_method_required` and a neutral `message`, without `checkoutUrl` or session creation; subscription Checkout returns `url`. Inspect the endpoint-specific error type.

Top-ups require `successUrl` on every request and a positive whole-cent multiple of 10,000 micro-dollar credits. For example, 2,500,000 credits buys $2.50 before fees; `totalCents` is 289 for that example. It is not a credit count. `isPro` means effective Pro only, and is false for paid Starter; use `plan` for paid-plan decisions.

Scheduled task creation and updates can return a plan-limit 402; task reads do not run that gate. Free/Starter/Pro are technical entitlement values. Platform/Advisory/Build/Enterprise marketing offers describe commercial services; they are not interchangeable names.
