{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/spicrawl-com/json-schema/spicrawl-com-batch-create-request-schema.json",
  "title": "BatchCreateRequest",
  "description": "Body of POST /v1/batch (and of POST /v1/batch/{batchID}/items). Exactly one of `urls` or `items` must be non-empty.\nTop-level BatchScrapeParams fields are job-wide defaults. A top-level `url` is rejected with 400 — put targets in\n`urls` or `items[].url`. Unknown fields are a 400. Must be ≤ 1 MiB; ≤ 10,000 targets.\n",
  "x-generated": "2026-10-31",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/spicrawl-com-openapi.yaml#/components/schemas/BatchCreateRequest",
  "allOf": [
    {
      "$ref": "#/$defs/BatchScrapeParams"
    }
  ],
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Free-text label shown on the job object and in the dashboard. No uniqueness; omit for none."
    },
    "urls": {
      "type": "array",
      "minItems": 1,
      "maxItems": 10000,
      "items": {
        "type": "string",
        "format": "uri"
      },
      "description": "Shorthand list of targets that all use the job-level settings. Mutually exclusive with `items` (both → 400 INCOMPATIBLE_FLAGS; neither → 400 MISSING_PARAMETER). Order defines `seq` (0-based)."
    },
    "items": {
      "type": "array",
      "minItems": 1,
      "maxItems": 10000,
      "items": {
        "$ref": "#/$defs/BatchItem"
      },
      "description": "Long form, one object per target with optional per-item overrides and `external_id`. Mutually exclusive with `urls`. Order defines `seq` (0-based)."
    },
    "concurrency": {
      "type": "integer",
      "default": 10,
      "description": "Max items of this job in flight at once. Values below 1 become 1; values above 50 are clamped to 50 and reported in the job's `warnings` (not an error)."
    },
    "priority": {
      "type": "integer",
      "minimum": 0,
      "maximum": 1000,
      "default": 100,
      "description": "Scheduling priority relative to your other jobs. Outside 0-1000 is a 400."
    },
    "max_attempts": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10,
      "default": 3,
      "description": "Attempts per item before it is marked failed (only retryable failures are re-attempted). Outside 1-10 is a 400."
    },
    "failure_threshold": {
      "type": "integer",
      "format": "int64",
      "minimum": 1,
      "description": "Abort the job as `failed` once this many items have failed. Omit to never abort early. 0 or negative is a 400."
    },
    "credit_budget": {
      "type": "integer",
      "minimum": 1,
      "description": "Ceiling on the WHOLE run in whole credits; the worker stops charging work beyond it. Omit to use the job's projected cost; an `open` job then has no ceiling, because its projection covers only the items it was submitted with. Stored as given in `params.credit_budget_micro`. 0 or negative is a 400. Different from `max_cost`, which is per item."
    },
    "webhook_endpoint_id": {
      "type": "string",
      "description": "Id of a webhook endpoint registered for this organization; it receives the job object when the job finishes (notify-only — fetch results yourself). A value that is not a valid id is a 400; blank is treated as absent."
    },
    "open": {
      "type": "boolean",
      "default": false,
      "description": "Keep the job accepting more items via POST /v1/batch/{batchID}/items. An open job never completes on its own — call POST /v1/batch/{batchID}/close when done. Total across appends is capped at 100,000 items."
    }
  },
  "unevaluatedProperties": false,
  "$defs": {
    "BatchItem": {
      "description": "One URL of a batch: a /v1/scrape request object (every BatchScrapeParams field plus `url`) and an optional\n`external_id`. Fields set here override the job-level value for this item only. Unknown fields are a 400.\nRun controls (`concurrency`, `priority`, etc.) are not accepted on an item.\n",
      "allOf": [
        {
          "$ref": "#/$defs/BatchScrapeParams"
        }
      ],
      "type": "object",
      "required": [
        "url"
      ],
      "properties": {
        "url": {
          "$ref": "#/$defs/url",
          "description": "The target URL for this item, validated exactly as /v1/scrape's `url` (http/https, SSRF and blocklist checks). Stored up to 8,192 characters."
        },
        "external_id": {
          "type": "string",
          "maxLength": 255,
          "description": "Your correlation id, echoed verbatim on this item's result line and never sent to the target. Trimmed of whitespace; empty means none. Not required to be unique."
        }
      },
      "unevaluatedProperties": false
    },
    "BatchScrapeParams": {
      "type": "object",
      "description": "The /v1/scrape request object as accepted by batch, minus `url`. Field names, types and validation are exactly\nScrapeRequest's (enforced by a parity test); each property below references it. Every item is merged as\njob-level value, overridden by the item's value (an explicit `false` on an item wins), with the project's\ndashboard defaults underneath both. Validation happens for every item at submission.\n\nWhat the batch worker executes: each item is one fetch or one render, and the document is returned as\n`response_format` (`html` by default, `json`, `markdown` or `text`). A field is either honoured on a batch item\nor refused at submission, never accepted and skipped. Refused with 400 INVALID_PARAMETER, naming every offending\nfield, with nothing queued or charged: `actions`, `screenshot` and its options, `network_capture`, `extract`,\n`extract_preset`, `ai_extract`, `autoparse`, `links`, `session_id`, `cache`/`cache_ttl`, `original_status`,\n`allowed_status_codes`, `parse_pdf=true`, `mode=auto`, `response_format=pdf`, and a non-GET `method` with\n`js_render` or `stealth`. A value that asks for nothing (`screenshot: false`, an empty list) is accepted. A\nrefusal caused by a project default says so in `detail`. Use /v1/scrape for those fields.\n\nA batch item is priced when it is accepted, so a bot challenge never escalates it to stronger rendering or retries it on a\ndifferent exit the way it can a /v1/scrape request with your own `proxy`.\n",
      "properties": {
        "method": {
          "$ref": "#/$defs/method",
          "description": "Honoured on fetched items. A method other than GET with `js_render` or `stealth` is refused with 400, because a browser navigation is always a GET."
        },
        "js_render": {
          "$ref": "#/$defs/js_render",
          "description": "Run the page's JavaScript before capture. Honoured by the batch worker. Default false (or the project default)."
        },
        "stealth": {
          "$ref": "#/$defs/stealth",
          "description": "Coming soon. Render with full-browser rendering hardened against bot detection; takes precedence over `js_render`. Honoured by the batch worker. Default false (or the project default)."
        },
        "impersonate": {
          "$ref": "#/$defs/impersonate",
          "description": "Applied by the batch worker on fetched items. Default true, as on /v1/scrape; false on the item, the job or the project defaults opts out."
        },
        "mode": {
          "$ref": "#/$defs/mode",
          "description": "`auto` is refused on batch with 400 INVALID_PARAMETER, also when it comes from the project defaults; the batch worker does not escalate. It runs the rendering that `js_render` or `stealth` selects."
        },
        "engine": {
          "$ref": "#/$defs/engine",
          "deprecated": true,
          "description": "Deprecated and ignored; rendering is chosen automatically. The job's `warnings` report the ignored field."
        },
        "premium_proxy": {
          "$ref": "#/$defs/premium_proxy",
          "description": "Coming soon. Use the residential proxy pool instead of datacenter. Honoured by the batch worker; ignored when `proxy` is set."
        },
        "proxy_country": {
          "$ref": "#/$defs/proxy_country",
          "description": "Coming soon. Exit country for pool proxies. Honoured by the batch worker."
        },
        "proxy": {
          "$ref": "#/$defs/proxy",
          "description": "Your own proxy URL; wins over the pool flags. Honoured by the batch worker."
        },
        "proxy_verify": {
          "$ref": "#/$defs/proxy_verify",
          "description": "Honoured by the batch worker: the custom proxy is probed before the engine cost is spent."
        },
        "session_id": {
          "$ref": "#/$defs/session_id",
          "description": "Batch items do not run inside a session. Refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape."
        },
        "sticky_key": {
          "$ref": "#/$defs/sticky_key",
          "description": "Coming soon. Honoured by the batch worker: equal keys are pinned to the same pool exit."
        },
        "wait": {
          "$ref": "#/$defs/wait",
          "description": "Honoured on rendered items."
        },
        "wait_for": {
          "$ref": "#/$defs/wait_for",
          "description": "Honoured on rendered items."
        },
        "wait_for_timeout": {
          "$ref": "#/$defs/wait_for_timeout",
          "description": "Honoured on rendered items."
        },
        "actions": {
          "$ref": "#/$defs/actions",
          "description": "The batch worker does not run browser actions. A non-empty list is refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape."
        },
        "block_resources": {
          "$ref": "#/$defs/block_resources",
          "description": "Resource classes to block while rendering. Honoured on rendered items only; unset blocks nothing on full-browser rendering, `none` blocks nothing, a list blocks the classes named. Falls back to the project default when unset."
        },
        "headless": {
          "$ref": "#/$defs/headless",
          "deprecated": true,
          "description": "Deprecated and ignored. The browser mode is chosen automatically."
        },
        "screenshot": {
          "$ref": "#/$defs/screenshot",
          "description": "The batch worker returns no screenshot. `true` is refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape."
        },
        "screenshot_fullpage": {
          "$ref": "#/$defs/screenshot_fullpage",
          "description": "Refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape."
        },
        "screenshot_selector": {
          "$ref": "#/$defs/screenshot_selector",
          "description": "Refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape."
        },
        "screenshot_format": {
          "$ref": "#/$defs/screenshot_format",
          "description": "Refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape."
        },
        "screenshot_quality": {
          "$ref": "#/$defs/screenshot_quality",
          "description": "Refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape."
        },
        "custom_headers": {
          "$ref": "#/$defs/custom_headers",
          "description": "Honoured by the batch worker on every item. Falls back to the project default when unset."
        },
        "autoparse": {
          "$ref": "#/$defs/autoparse",
          "description": "Refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape."
        },
        "links": {
          "$ref": "#/$defs/links",
          "description": "Refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape. Also refused when it comes from the project defaults, and `detail` says so."
        },
        "network_capture": {
          "$ref": "#/$defs/network_capture",
          "description": "Refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape."
        },
        "extract": {
          "$ref": "#/$defs/extract",
          "description": "Refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape."
        },
        "extract_preset": {
          "$ref": "#/$defs/extract_preset",
          "description": "Refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape."
        },
        "ai_extract": {
          "$ref": "#/$defs/ai_extract",
          "description": "Refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape."
        },
        "main_content_only": {
          "$ref": "#/$defs/main_content_only",
          "description": "Honoured on `markdown` and `text` items. Unset means off. Falls back to the project default when unset."
        },
        "include_tags": {
          "$ref": "#/$defs/include_tags",
          "description": "Honoured on `markdown` and `text` items."
        },
        "exclude_tags": {
          "$ref": "#/$defs/exclude_tags",
          "description": "Honoured on `markdown` and `text` items."
        },
        "parse_pdf": {
          "$ref": "#/$defs/parse_pdf",
          "description": "The batch worker has no PDF parser. `true` is refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape. `false` is accepted."
        },
        "response_format": {
          "$ref": "#/$defs/response_format",
          "description": "Honoured. `html` (the default) and `json` return the document as retrieved; `markdown` and `text` run the same conversion as /v1/scrape. A document that is not HTML (a PDF, an image, JSON) fails a `markdown` or `text` item with EXTRACTION_FAILED; request `html` to get it as retrieved. `pdf` is refused with 400, also when it comes from the project defaults."
        },
        "cache": {
          "$ref": "#/$defs/cache",
          "description": "Batch never reads from the result cache. `true` is refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape."
        },
        "cache_ttl": {
          "$ref": "#/$defs/cache_ttl",
          "description": "A non-zero value is refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape."
        },
        "max_cost": {
          "$ref": "#/$defs/max_cost",
          "description": "Per-ITEM credit ceiling, checked at submission against the item's dearest outcome (400 if exceeded). For a ceiling on the whole run use `credit_budget`."
        },
        "original_status": {
          "$ref": "#/$defs/original_status",
          "description": "Refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape. Result lines always carry the target status in `http_status`."
        },
        "allowed_status_codes": {
          "$ref": "#/$defs/allowed_status_codes",
          "description": "Refused on batch with 400 INVALID_PARAMETER (nothing queued or charged); use /v1/scrape. Billing is fixed to target statuses 200, 404 and 410."
        }
      }
    }
  }
}