# Estimate the credit cost of a job without enqueuing it

POST /v1/jobs/estimate: Estimate the credit cost of a job without enqueuing it

```http
POST /v1/jobs/estimate
```

|  |  |
|---|---|
| Operation id | `estimateJob` |
| Key scope | `read`: every key |
| Section | [Jobs](https://docs.kaab.ist/reference/api#jobs) |

## Request body

| Field | Type | Required | Description |
|---|---|---|---|
| `program` | string |  | min length 1 |
| `inputs` | map of string or number or boolean |  |  |
| `definition` | [ProgramDefinition](https://docs.kaab.ist/reference/api/schemas/program-definition) |  |  |
| `url` | string (uri) |  |  |
| `extract` | [ExtractSchema](https://docs.kaab.ist/reference/api/schemas/extract-schema) |  |  |
| `session` | string |  | min length 1 |
| `options` | object |  |  |
| `options.region` | `"eu"`, `"us"` |  | The execution region, `eu` or `us`. Defaults to your organisation's region. |
| `options.max_tier` | `"http"`, `"browser"`, `"stealth"` |  | The most expensive tier the job may escalate to: `http`, `browser` or `stealth`. Defaults to your plan's maximum; a page that needs more fails the job with `max_tier_exceeded`. |
| `options.prefer_tier` | `"http"`, `"browser"`, `"stealth"` |  | The tier the job starts at: `http`, `browser` or `stealth`. Defaults to `http` for a program that only uses `goto` and `extract`, and `browser` otherwise; a site profile may raise it. |
| `options.assets` | `"block"`, `"allow"` |  | Whether browser tiers load images, fonts and media: `block` (the default) or `allow`. |
| `options.block_urls` | string[] |  | Up to 50 URL globs, matched against the full URL, whose requests a browser tier aborts before they are sent, whatever `assets` says. Use it for a site's own analytics; a pattern that matches something the page needs breaks the page, and the `http` tier ignores it. · max items 50 |
| `options.proxy` | object |  | Which proxy the job's traffic goes through. Defaults to `{ "mode": "platform", "fallback": "none" }`. |
| `options.proxy.mode` | `"platform"`, `"byo"` |  | `platform` uses Kaabist's proxy pool; `byo` uses a proxy your organisation registered. Defaults to `platform`. |
| `options.proxy.byo_id` | string |  | The `byo_…` id of the bring-your-own proxy to use with `mode: "byo"`. Without it, `byo` uses your organisation's default proxy. · pattern `^byo_[0-9a-hjkmnp-tv-z]{26}$` |
| `options.proxy.country` | string |  | The exit country, as an uppercase ISO-3166-1 alpha-2 code such as `DE`. · pattern `^[A-Z]{2}$` |
| `options.proxy.pool` | `"datacenter"`, `"residential"`, `"mobile"` |  | The platform proxy class: `datacenter`, `residential` or `mobile`. |
| `options.proxy.fallback` | `"platform"`, `"none"` |  | What happens when a bring-your-own proxy fails. `none` (the default) fails the job with `byo_proxy_unreachable`; `platform` runs on the platform pool, adds the warning `byo_fallback_used` and bills the proxy traffic. |
| `options.llm` | object |  | The language model that heals locators and classifies unrecognised pages. Defaults to your organisation's configured model, else the platform's model when it is enabled. |
| `options.llm.byo_id` | string |  | The `byo_…` id of a bring-your-own model. Only `capable` models heal, and a bring-your-own model's tokens are not billed. · pattern `^byo_[0-9a-hjkmnp-tv-z]{26}$` |
| `options.captcha` | object |  | The captcha solver `policies.captcha: "solve"` uses. Defaults to your organisation's configured solver, else the platform's. |
| `options.captcha.byo_id` | string |  | The `byo_…` id of a bring-your-own captcha solver. Its solves are not billed. · pattern `^byo_[0-9a-hjkmnp-tv-z]{26}$` |
| `options.session` | string |  | The name of a session to bind. Its storage state is loaded before step 1, so the job starts signed in. · min length 1 |
| `options.resume_from_session` | boolean |  | When `true` and the bound session is valid, the job skips ahead to the step after the `checkpoint` that saved this session, instead of signing in again. Defaults to `false`. |
| `options.session_probe` | `"http"`, `"browser"` |  | Which tier checks the bound session before the job runs: `http` (the default, one request) or `browser`, for a site whose signed-in landmark needs JavaScript. A `browser` probe is billed as a browser page load. |
| `options.cache` | object |  | Lets the job reuse a recent snapshot of the same page. Ignored when the program binds a session or has secret inputs. |
| `options.cache.max_age` | integer | yes | The oldest snapshot the job accepts, in seconds, from `0` to `86400`. · ≥ 0 · ≤ 86400 |
| `options.viewport` | object |  | The browser window size for browser tiers. Defaults to 1280×800. |
| `options.viewport.width` | integer | yes | Viewport width in pixels, from `320` to `3840`. · ≥ 320 · ≤ 3840 |
| `options.viewport.height` | integer | yes | Viewport height in pixels, from `240` to `2160`. · ≥ 240 · ≤ 2160 |
| `options.browser_profile` | `"desktop"`, `"mobile"` |  | The user agent and viewport bundle: `desktop` (the default) or `mobile`. |
| `options.engine` | `"chromium"`, `"firefox"` |  | The stealth tier's browser. Without it, a stealth job starts on a hardened Chromium and switches to Camoufox (a Firefox build) for a hostname where Chromium was refused. `firefox` always runs Camoufox; `chromium` never switches. The browser tier always runs Chromium. |
| `options.fill_mode` | `"insert"`, `"type"` |  | How `fill` puts text into a field at a browser tier. `insert` (the default) commits the whole value at once, as a paste does; `type` sends one key event per character, for fields with an input mask or a per-keystroke validator. |
| `options.follow_popups` | boolean |  | Browser tiers: when `true`, a page the program opens in a popup (`window.open`, `target=_blank`) is opened in the same tab instead, so a one-time sign-in token is spent by the tab the program drives. Defaults to `false`. |
| `options.locale` | string |  | The browser locale as a BCP-47 tag. Defaults to `en-US`, or to the locale of `proxy.country` when one is set. · min length 2 |
| `options.timezone` | string |  | The browser time zone as an IANA name. Defaults to `UTC`, or to the time zone of `proxy.country` when one is set. · min length 1 |
| `options.max_duration_ms` | integer |  | The whole job's time budget in milliseconds, from `1000` to `3600000`, not counting time spent parked. Defaults to your plan's limit. · ≥ 1000 · ≤ 3600000 |
| `options.max_credits` | integer |  | The most credits the job may spend; exceeding it fails the job. Defaults to your plan's limit. · ≥ 1 |
| `options.max_steps` | integer |  | The most steps the job may execute, from `1` to `200`. Defaults to `200`. · ≥ 1 · ≤ 200 |
| `options.headers` | map of string |  | Extra request headers, as a map of name to value, sent by the `http` tier and, on the browser tiers, on the page's own top-level navigations only: never on its images, scripts or `fetch` calls, nor to another host the page talks to. |
| `options.canary` | object |  | The canary probe that runs the program on a schedule to notice when a site changes. The stored canary settings on the program override these key by key. |
| `options.canary.enabled` | boolean |  | `true` opts in and `false` opts out. When absent, a program used in the last seven days that has a `canary.url` or is only `goto` and `extract` gets a canary. |
| `options.canary.cron` | string |  | The schedule the canary runs on, as a cron expression such as `0 */6 * * *`. · min length 1 |
| `options.canary.url` | string |  | The URL the canary runs against, a template. It is bound as the input named `url`, which `{{inputs.url}}` reads. |
| `options.canary.inputs` | map of any |  | Input values the canary runs with. Credentials do not belong here; they are stored separately on the program. |
| `options.obstacles` | object |  | How the obstacle layer treats cookie banners, dialogs, overlays, toasts and tours. Defaults to `{ "allow_hide": true }`. |
| `options.obstacles.allow_hide` | boolean |  | Whether a `dialog` or `consent` obstacle that cannot be dismissed may be hidden as a last resort. Defaults to `true`; set `false` for a page whose modal is the page, such as an age gate or a paywall, so the job parks for a person instead. |
| `options.obstacles.ignore` | string[] |  | Up to 20 CSS selectors the obstacle probe never reports, for elements such as a chat bubble or a sticky header that are part of the page. · max items 20 |
| `policies` | object |  |  |
| `policies.strict` | boolean |  | When `true`, any warning fails the job, including a failing `$asserts` entry. Defaults to `false`. |
| `policies.include_raw` | boolean |  | When `true`, the result carries the raw string of every field, not only of fields that failed or came out null, and `vars` are returned under `meta.vars`. Defaults to `false`. |
| `policies.on_session_expired` | `"fail"`, `"park"`, `"refresh"` |  | What a job does when its session has expired: `refresh` (the default) runs the session's refresh program and carries on, `park` stops as `needs_attention` for a person to sign in, `fail` fails with `session_expired`. |
| `policies.captcha` | `"park"`, `"solve"`, `"fail"` |  | What a job does when it meets a captcha: `park` (the default) waits for a person, `solve` buys a solve from the configured solver, `fail` fails the job. |
| `policies.screenshot` | `"never"`, `"on_failure"`, `"always"` |  | When steps are screenshotted: `never`, `on_failure` (the default) or `always`. |
| `webhook_url` | string (uri) |  |  |
| `tags` | string[] |  | max items 20 |
| `dry_run` | boolean |  |  |
| `cache` | object |  |  |
| `cache.max_age` | integer | yes | ≥ 0 · ≤ 86400 |

## Example request

```bash
curl -X POST "https://api.kaab.ist/v1/jobs/estimate" \
  -H "Authorization: Bearer $KAAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

## Responses

### 200

Estimate

| Field | Type | Required | Description |
|---|---|---|---|
| `credits_min` | number | yes |  |
| `credits_max` | number | yes |  |
| `tier_expected` | `"http"`, `"browser"`, `"stealth"` | yes |  |
| `cache` | `"possible"`, `"no"` | yes |  |

## Errors

Every error answers with the [error envelope](https://docs.kaab.ist/reference/api/schemas/error-response); its `code` is one of the [error codes](https://docs.kaab.ist/reference/codes#errors).

| Status | When |
|---|---|
| 400 | `request_invalid`: request validation failed |
| 401 | Missing or invalid API key |
| 403 | The key's scope does not cover this operation |
| 429 | Rate limited; see `Retry-After` |
| 500 | `internal`: unexpected server error |
