# Credits

> What each Spicrawl request costs by engine, what is free, and how to cap spend with max_cost and batch budgets.

Source: https://docs.spicrawl.com/credits

You pay credits only for a successful request. The price depends on the engine that ran. `ai_extract` (coming soon) adds 4. Failures, cache hits and non-billable target statuses cost 0. The `X-Credits-Charged` header on every response tells you what that request billed, and `X-Credits-Remaining` what is left of your monthly allowance after it.

```bash
curl -sS -D - -o /dev/null "https://api.spicrawl.com/v1/scrape" \
  -H "Authorization: Bearer $SPICRAWL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/products/42", "js_render": true, "max_cost": 3}' \
  | grep -i -E '^(x-credits-charged|x-request-cost|x-credits-remaining|x-engine|cache-state):'
```

```text
X-Credits-Charged: 3
X-Request-Cost: 3
X-Credits-Remaining: 976
X-Engine: obscura
Cache-State: miss
```

## Price table

Credits per successful request.

| Engine                                     | How you select it       | Credits              |
| ------------------------------------------ | ----------------------- | -------------------- |
| `fetch`                                    | Default (no JavaScript) | 1                    |
| `obscura`                                  | `js_render=true`        | 3                    |
| `chromium`                                 | `engine=chromium`       | 8                    |
| Remote browser session (`GET /v1/browser`) | CDP connect             | 8 per started minute |

* **Your own proxy** (`proxy`) adds no proxy surcharge. Spicrawl's [managed residential exits](https://docs.spicrawl.com/guides/proxies-and-geo.md#managed-proxy-pool-coming-soon) (coming soon) are not available yet.
* **`ai_extract`** (coming soon) adds 4 credits on top of the engine price. It is the only additive item.
* **`mode=auto`** tries `fetch`, then `obscura`, and bills only the rung that succeeded. If every rung fails, it bills 0.
* **A bot challenge through your own `proxy`** (or a `premium_proxy` (coming soon) exit) can move the request to the Camoufox stealth browser (coming soon), billed the same way: 25 credits if Camoufox returned the page, the first engine's price if that engine returned it, 0 if every rung was challenged. See [Anti-bot](https://docs.spicrawl.com/guides/anti-bot.md#automatic-escalation-coming-soon).

## Remote browser sessions

A `GET /v1/browser` CDP session is priced the same as one real-Chromium page: **8 credits per started minute** (a 10-second session is 1 minute = 8 credits). At admission the session holds the price of its whole `session_ttl` against your monthly allowance (default `session_ttl` 180 s = 24 credits; the 60–900 s range = 8–120 credits); if the remaining allowance cannot cover that hold, the session is refused with `402 ERR::LIMIT::QUOTA_EXCEEDED` before any browser is leased, and a shorter `session_ttl` may fit. At close, the session is charged for the minutes it actually held (capped at `session_ttl`'s minutes) and the unused part of the hold is returned immediately. A session the platform ends (the browser backend dropped it, or the API shut down) costs 0. A session that never opens costs 0 and returns its hold.

Free, at any engine:

| Feature                         | Extra credits |
| ------------------------------- | ------------- |
| `impersonate` (on by default)   | 0             |
| `extract` (selector extraction) | 0             |
| `autoparse`                     | 0             |
| `links`                         | 0             |
| `network_capture`               | 0             |
| Markdown, text, JSON envelope   | 0             |

## What costs 0

| Outcome                                                                                          | Charged                                          |
| ------------------------------------------------------------------------------------------------ | ------------------------------------------------ |
| Any error response (`application/problem+json`), including bot challenges and timeouts           | 0                                                |
| Cache hit (`Cache-State: hit`)                                                                   | 0                                                |
| Target answered with a status other than `200`, `404` or `410` and not in `allowed_status_codes` | 0. You still get HTTP `200` and the site's body. |
| A requested screenshot that was not delivered                                                    | Reported in `warnings`, charged 0                |
| Refused before running: validation errors, `ERR::LIMIT::MAX_COST_EXCEEDED`, limits               | 0                                                |

Target statuses `200`, `404` and `410` are billable successes. Add others with `allowed_status_codes`, for example `[403]` if a `403` page is the content you want.

Retrying a failed request costs nothing extra, but `POST /v1/scrape` is not idempotent: repeating a request that succeeded bills it again unless it is served from the cache.

## Cap a request with max\_cost

`max_cost` is a credit ceiling for one request. `0` (the default) means no ceiling. Spicrawl checks it against the dearest outcome the request could reach before anything runs. If that outcome is over the ceiling, the request fails with `400 ERR::LIMIT::MAX_COST_EXCEEDED` at 0 credits.

| Request                           | Checked against                 |
| --------------------------------- | ------------------------------- |
| `{"url": "…"}`                    | 1                               |
| `{"url": "…", "js_render": true}` | 3                               |
| `{"url": "…", "mode": "auto"}`    | 25 (the top rung of the ladder) |

> **Tip:** Under `mode=auto`, `max_cost: 3` fails every time, because the top rung costs 25. To stay under 3, send `js_render=true` instead of `mode=auto`.

The move to Camoufox on a bot challenge (coming soon) never fails this check. With `max_cost` below 25 the request runs without it, at the price of its first engine.


## Read the bill in headers

| Header                | Meaning                                                                                                                                                                             |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-Credits-Charged`   | Credits actually billed for this request. `0` on any failure, cache hit or non-billable target status.                                                                              |
| `X-Request-Cost`      | Price of the engine that ran. `0` on a cache hit.                                                                                                                                   |
| `X-Credits-Remaining` | Credits left in your monthly allowance after this request, rounded down. Absent when your organization has no monthly limit. See [Monthly credit ceiling](#monthly-credit-ceiling). |
| `X-Engine`            | Engine that ran: `fetch`, `obscura` or `chromium`. Under `mode=auto`, the rung that produced the result.                                                                            |
| `X-Proxy-Source`      | `custom` (your proxy) or `direct`.                                                                                                                                                  |

`X-Credits-Charged` and `X-Request-Cost` differ exactly when nothing was billed: a site answering `403` gives `X-Request-Cost: 3` and `X-Credits-Charged: 0` on an `obscura` render. The JSON envelope repeats the charge as `credits`.

## Monthly credit ceiling

Each organization has a monthly credit ceiling (the beta has one plan for everyone: 1,000 credits/month). The allowance month is the org's own anniversary month: it runs from the day the org was created and resets at 00:00 UTC on that day every month (an org created on the 29th, 30th or 31st resets on the last day of a shorter month). Before a request runs, Spicrawl reserves its dearest possible cost against the allowance. For a request that could move to Camoufox on a bot challenge (coming soon), that is 25; if the allowance covers the first engine but not 25, the request runs without the move. When the allowance is used up, requests fail with `402 ERR::LIMIT::QUOTA_EXCEEDED` (not retryable), naming the exact reset date, until the ceiling is raised or the allowance resets. See [Errors](https://docs.spicrawl.com/errors.md#LIMIT_QUOTA_EXCEEDED).

To see how many credits are left, read `X-Credits-Remaining` on a `/v1/scrape` response, or on a batch submit, append or retry response. It is the allowance left after that request's charge (for a batch call, after its hold), in whole credits rounded down, and it counts credits held for work in flight the same way `allowance.used_micro` below does. It is never negative; on a `402` it can be above `0`, meaning less than that request needed. It is absent when your organization has no monthly limit, when the request was refused before the credit check (a validation error, a rate or concurrency limit), or when the balance could not be read at that moment.

For the full picture, or without a request to read the header from, call `GET /v1/usage/summary` (needs a key with the `read` scope; 1 credit = 1,000,000 micro-credits):

* `allowance` is the ceiling as it is enforced: `limit_micro`, `used_micro`, `remaining_micro`, `limit_source` (`default`, or `override` when your organization has its own limit), `window_start` and `resets_at`. `allowance.remaining_micro` is the number to show as "credits left". Its `used_micro` counts credits currently held for work in flight (an open browser session holds its whole `session_ttl` price; a batch job holds its unfinished items' prices) as well as credits spent.
* `credits` is metered usage for the period from the daily rollup: `included_micro`, `used_micro`, `remaining_micro`. It counts spent credits only, batch items included, but not credits held for work still running, so it can be lower than `allowance.used_micro`.

An organization with no subscription is on the free plan: `plan.code` is `free`, `plan.subscription_status` is `none`, `plan.period_source` is `allowance_month`, and `credits.included_micro` is the monthly ceiling.

## Batch budgets

A batch job is billed per item, only for items that succeed, at the same prices as `/v1/scrape`. Submitting a job charges nothing (`X-Credits-Charged: 0`), but its `X-Credits-Remaining` already counts the whole hold (`estimated_credits`) as used.

| Field                      | Where              | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| -------------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `max_cost`                 | request, per item  | Ceiling for each item, as on `/v1/scrape`. Checked at submission: an item whose dearest outcome is over it fails the whole submission with `400 ERR::LIMIT::MAX_COST_EXCEEDED`, with `Item N:` in `detail`.                                                                                                                                                                                                                                                                  |
| `credit_budget`            | request, whole job | Ceiling for the whole run in whole credits. The worker stops charging work beyond it. Omit it to use the job's projected cost. An `open` job has no ceiling unless you set one, because its projection covers only the items it was submitted with.                                                                                                                                                                                                                          |
| `estimated_credits`        | job object         | Credits reserved against the monthly allowance at submission: the dearest outcome of every item. It is a hold, not a charge; credits held for items that fail, are cancelled or skipped are returned within about a minute of the item finishing. Items appended to an open job are reserved the same way when they are added; the append response's `X-Request-Cost` is their total, and it fails with `402 ERR::LIMIT::QUOTA_EXCEEDED` if the allowance cannot cover them. |
| `progress.credits_charged` | job object         | Credits actually charged so far.                                                                                                                                                                                                                                                                                                                                                                                                                                             |

Batch items apply only `js_render`, `proxy` and `block_resources` today and return the raw page, so `ai_extract` is never added to a batch item's price. See [Batch](https://docs.spicrawl.com/guides/batch.md).
