Schedules
Create a cron schedule for a program
POST /v1/schedules: Create a cron schedule for a program
POST /v1/schedules
| Operation id | createSchedule |
| Key scope | run: keys that run jobs (and admin keys) |
| Section | Schedules |
#Request body
| Field | Type | Required | Description |
|---|---|---|---|
program | string | yes | min length 1 |
inputs | map of string or number or boolean | ||
cron | string | yes | pattern ^\S+\s+\S+\s+\S+\s+\S+\s+\S+$ |
timezone | string | min length 1 · default "UTC" | |
change_detection | object | ||
change_detection.enabled | boolean | yes | |
change_detection.fields | string | ||
webhook_url | string (uri) | ||
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 | |
overlap_policy | "skip", "queue" | default "skip" | |
catch_up | boolean | default false |
#Example request
curl -X POST "https://api.kaab.ist/v1/schedules" \
-H "Authorization: Bearer $KAAB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"program":"acme-report@production","inputs":{"report_id":"42"},"cron":"*/30 * * * *","timezone":"Europe/Istanbul","change_detection":{"enabled":true,"fields":["price"]},"webhook_url":"https://hooks.acme.test/kaab"}'
#Responses
#201
Schedule
{
"id": "sch_01j9zqk3q7m4n5p6r7s8t9v0wx",
"object": "schedule",
"program": "acme-report@production",
"inputs": {
"report_id": "42"
},
"cron": "*/30 * * * *",
"timezone": "Europe/Istanbul",
"status": "active",
"paused_reason": null,
"mode": "live",
"change_detection": {
"enabled": true,
"fields": [
"price"
]
},
"overlap_policy": "skip",
"catch_up": false,
"webhook_url": "https://hooks.acme.test/kaab",
"last_run_at": "2026-09-18T10:00:00Z",
"next_run_at": "2026-09-18T10:30:00Z",
"created_at": "2026-09-18T10:00:00Z"
}
#Errors
Every error answers with the error envelope; its code is one of the error codes.
| Status | When |
|---|---|
| 400 | request_invalid: request validation failed |
| 401 | Missing or invalid API key |
| 402 | insufficient_credits: top up to continue |
| 403 | The key's scope does not cover this operation |
| 409 | Conflicting state (see error.code) |
| 422 | Semantically invalid request (see error.code) |
| 429 | Rate limited; see Retry-After |
| 500 | internal: unexpected server error |