URL: https://bytekit.com/docs/changelog # Changelog Release history for the ByteKit API and SDKs _No releases yet. Check back after the first version ships._ --- URL: https://bytekit.com/docs/ # ByteKit The web data API for AI agents and pipelines — scrape, screenshot, record, and monitor any URL through one endpoint. import { Rocket, Braces, FileCode2, Binary, FileText, Camera, Video, Bell, Layers, Map, Zap, Bot, Network } from 'lucide-react'; ByteKit is a REST API for capturing what a webpage looks like and what it says. One key, one base URL: scrape content, take screenshots, record scrolling video, discover sitemaps, and watch URLs for changes — with each request automatically routed through the right rendering path. ```bash title="Your first request" curl -X POST https://api.bytekit.com/v1/scrape \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com", "formats": ["markdown"]}' ``` ## Start building - [Quickstart](https://bytekit.com/docs/quickstart) — Make your first scrape or screenshot with curl. No SDK required. - [TypeScript SDK](https://bytekit.com/docs/libraries/typescript) — Type-safe client for Node.js and modern JavaScript runtimes. - [Python SDK](https://bytekit.com/docs/libraries/python) — Official Python client with sync and async support. - [Go](https://bytekit.com/docs/libraries/go) — Call the ByteKit REST API from Go with the standard library. ## Capabilities - [Scrape](https://bytekit.com/docs/api/scrape/createscrape) — Fetch any URL as raw HTML, clean markdown, or structured content. - [Screenshots](https://bytekit.com/docs/api/screenshots/createscreenshot) — Capture full-page or viewport PNG/JPEG. - [Recordings](https://bytekit.com/docs/api/recordings/createrecording) — Generate a scrolling video of a page. - [Monitors](https://bytekit.com/docs/api/monitors/createmonitor) — Watch a URL on a schedule and webhook you when it changes. - [Bulk](https://bytekit.com/docs/api/bulk/createbulk) — Fan out thousands of URLs in parallel with per-item webhooks. - [Sitemap](https://bytekit.com/docs/api/sitemap/createsitemap) — Discover URLs from a domain's sitemap or by light crawling. - [Fetch](https://bytekit.com/docs/api/fetch/postfetch) — Low-latency raw HTTP fetch with optional markdown/HTML conversion. ## Built for agents Point your AI agent at the documentation index for a complete, machine-readable map of the API. - [llms-full.txt](https://bytekit.com/docs/llms-full.txt) — The entire documentation corpus as plain text for LLM ingestion. - [API Reference](https://bytekit.com/docs/api) — Every endpoint, request, and response. --- URL: https://bytekit.com/docs/introduction # Introduction A REST API for screenshots, scraping, and page-change monitoring — one key, one base URL, every output. ## What ByteKit is ByteKit is a REST API for capturing what a webpage looks like and what it says — screenshots, scrolling recordings, raw HTML or clean markdown, and page-change monitoring. One API key, one base URL, every output you'd otherwise stitch together from a headless-browser farm and a scraping proxy. ## What you can do with it - **Scrape** — Fetch a URL as raw HTML, clean markdown, or structured content. - **Screenshot** — Capture full-page or viewport PNG/JPEG. - **Record** — Generate a scrolling video of a page. - **Monitor** — Watch a URL on a schedule and webhook you when the rendered output changes. - **Bulk** — Fan out thousands of URLs in parallel and receive a webhook as each finishes. - **Sitemap** — Discover URLs from a domain's sitemap or by light crawling. Scrape calls accept an `"async": true` body field, in which case the API returns `202` with a `sc_…` ID that you poll or receive via webhook. Screenshots are synchronous by default (the call holds up to 28s and returns `200`); pass the `?async=true` query parameter to get back `202` with an `ss_…` ID immediately. Recordings are always async — they return `202` with a `rec_…` ID, with no `async` field to set. ## Authentication and accounts Every request carries a Bearer API key, prefixed `sk_live_`. Manage keys from the dashboard — up to 50 active keys per account, with per-key billing attribution so you can split usage cleanly across services. Full details in the [Authentication guide](https://bytekit.com/docs/guides/authentication). ## Where to go next - [Quickstart](https://bytekit.com/docs/quickstart) — first call in under five minutes, no SDK required. - [Client Libraries](https://bytekit.com/docs/libraries/typescript) — official TypeScript and Python SDKs. - [Scraping](https://bytekit.com/docs/guides/scraping) — formats, options, async polling. - [Rate Limits](https://bytekit.com/docs/guides/rate-limits) — quota model, concurrency slots, response headers. - [Monitors](https://bytekit.com/docs/guides/monitors) — schedule-based change detection with webhooks. - [API Reference](https://bytekit.com/docs/api) — every endpoint, request, and response. --- URL: https://bytekit.com/docs/mcp # MCP Server Call ByteKit web capture as a native tool from Claude, Cursor, and any MCP-compatible agent — hosted over HTTP or run locally over stdio. The ByteKit **MCP server** exposes scraping, search, screenshots, and account lookups as [Model Context Protocol](https://modelcontextprotocol.io) tools. Your agent calls `scrape_url` or `screenshot_url` the same way it calls any other tool — no wrapper to write, no headless browser to manage. There are two ways to connect: a **hosted HTTP endpoint** (nothing to install) and a **local stdio server** (`npx @hunt-labs/bytekit-mcp`). ## Authentication Both paths authenticate with a ByteKit API key. Sign up at [app.bytekit.com](https://app.bytekit.com) and create a key (prefixed `sk_live_`). The local server reads it from the `BYTEKIT_API_KEY` environment variable (or the `--api-key` flag); the hosted endpoint takes it as a bearer token. ## Option 1 — Hosted HTTP (no install) Point any MCP client that speaks the Streamable HTTP transport at: ``` https://api.bytekit.com/mcp ``` Send your API key as a bearer token. Example client config (Claude Desktop, Cursor, and most hosts accept this shape): ```json { "mcpServers": { "bytekit": { "url": "https://api.bytekit.com/mcp", "headers": { "Authorization": "Bearer $BYTEKIT_API_KEY" } } } } ``` ## Option 2 — Local stdio (`npx`) Run the server as a local subprocess over stdio. Nothing to install globally — `npx` fetches it on demand: ```bash npx @hunt-labs/bytekit-mcp --api-key $BYTEKIT_API_KEY ``` Or set the key in your environment and omit the flag: ```bash export BYTEKIT_API_KEY=sk_live_xxx npx @hunt-labs/bytekit-mcp ``` Client config for a stdio server: ```json { "mcpServers": { "bytekit": { "command": "npx", "args": ["-y", "@hunt-labs/bytekit-mcp"], "env": { "BYTEKIT_API_KEY": "sk_live_xxx" } } } } ``` ### API-key resolution order The local server resolves the key in this order: 1. `--api-key ` flag 2. `BYTEKIT_API_KEY` environment variable ## Tool inventory | Tool | What it does | | --- | --- | | `scrape_url` | Fetch a URL and return HTML, clean markdown, links, or images. | | `web_search` | Run a web search and return ranked organic results. | | `screenshot_url` | Capture a screenshot of a page (desktop/mobile, PNG/JPEG). | | `get_result` | Fetch a previously created screenshot by id. | | `get_account` | Return account details, plan, and current usage/quota. | | `list_docs` | List every page in the bundled ByteKit documentation. | | `search_docs` | Search the bundled documentation. | | `get_doc` | Fetch the full markdown of one documentation page. | The server also exposes **resources** (`bytekit://account`, `bytekit://screenshot/{id}`) and **prompts** (`summarize_webpage`, `extract_structured_data`, `research_topic`) that MCP hosts can surface directly. ## Next steps - [Quickstart](https://bytekit.com/docs/quickstart) — make your first ByteKit API call. - [Authentication](https://bytekit.com/docs/guides/authentication) — key formats and management. --- URL: https://bytekit.com/docs/quickstart # Quickstart Make your first ByteKit API call in under 5 minutes — no SDK required. Nothing to install. You only need `curl` and an API key. ## Get an API key Sign up at [app.bytekit.com](https://app.bytekit.com) and create an API key from the dashboard. Keys are prefixed `sk_live_`. See [Authentication](https://bytekit.com/docs/guides/authentication) for details on key formats and management. ## Make your first scrape Set your API key and run this — no file to save, no `chmod`: ```bash curl -X POST https://api.bytekit.com/v1/scrape \ -H "Authorization: Bearer $BYTEKIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url":"https://example.com"}' ``` Want a full-featured script with error handling and `jq` formatting? Use this instead: ```bash #!/usr/bin/env bash # POST /v1/scrape — scrape a URL and return its content as markdown. # Usage: BYTEKIT_API_KEY=sk_... bash examples/curl/scrape.sh set -euo pipefail : "${BYTEKIT_API_KEY:?BYTEKIT_API_KEY is required}" BASE_URL="${BYTEKIT_BASE_URL:-https://api.bytekit.com}" curl -sf -X POST "$BASE_URL/v1/scrape" \ -H "Authorization: Bearer $BYTEKIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com", "formats": ["markdown"]}' \ | jq . ``` A successful response looks like this: ```json { "status": "success", "id": "sc_...", "url": "https://example.com", "finalUrl": "https://example.com/", "contentType": "text/html; charset=UTF-8", "contentLength": 1256, "statusCode": 200, "retrievedAt": "2025-01-15T12:00:00Z", "formats": { "markdown": "# Example Domain\n\nThis domain is for use in illustrative examples..." }, "metadata": { "title": "Example Domain" } } ``` `contentLength` is the compressed wire size in bytes (used for bandwidth billing). The `id` field uses the `sc_` prefix — you can poll `GET /v1/scrape/{id}` if the scrape was queued asynchronously. ## Prefer an SDK? Skip the raw HTTP and use an official client: - [TypeScript SDK](https://bytekit.com/docs/libraries/typescript) — `npm install @hunt-labs/bytekit-sdk` - [Python SDK](https://bytekit.com/docs/libraries/python) — `pip install bytekit-sdk` ## Next steps - [Authentication](https://bytekit.com/docs/guides/authentication) — key formats, Bearer header, self-serve key management - [Scraping](https://bytekit.com/docs/guides/scraping) — formats, options, latency, and async polling - [Errors](https://bytekit.com/docs/guides/errors) — error shape, common codes, retry guidance - [Rate Limits](https://bytekit.com/docs/guides/rate-limits) — quota model, concurrency slots, rate limit headers - [Monitors](https://bytekit.com/docs/guides/monitors) — page-change detection (screenshot or scrape) with webhook notifications - [API Reference](https://bytekit.com/docs/api) — full endpoint documentation --- URL: https://bytekit.com/docs/api # API Reference Every ByteKit endpoint, request, and response. The ByteKit REST API is organized by capability. Every endpoint shares one base URL and Bearer authentication — start with the [Quickstart](https://bytekit.com/docs/quickstart) and [Authentication](https://bytekit.com/docs/guides/authentication) guides. - [Scrape](https://bytekit.com/docs/api/scrape/createscrape) — Fetch any URL as raw HTML, clean markdown, or structured content. - [Screenshots](https://bytekit.com/docs/api/screenshots/createscreenshot) — Capture full-page or viewport PNG/JPEG. - [Recordings](https://bytekit.com/docs/api/recordings/createrecording) — Generate a scrolling video of a page. - [Bulk](https://bytekit.com/docs/api/bulk/createbulk) — Fan out many URLs in parallel with per-item webhooks. - [Monitors](https://bytekit.com/docs/api/monitors/createmonitor) — Watch a URL on a schedule and webhook on change. - [Sitemap](https://bytekit.com/docs/api/sitemap/createsitemap) — Discover URLs from a domain's sitemap or by crawling. - [Fetch](https://bytekit.com/docs/api/fetch/postfetch) — Low-latency raw HTTP fetch with optional conversion. --- URL: https://bytekit.com/docs/cli/account # Account Account operations Account operations ### account › get Get the authenticated account | Option | Description | | --- | --- | | `-h, --help` | display help for command | ```bash $ bytekit account get ``` --- URL: https://bytekit.com/docs/cli/bulk # Bulk Bulk screenshot operations Bulk screenshot operations ### bulk › screenshots Bulk screenshot sub-resources | Option | Description | | --- | --- | | `-h, --help` | display help for command | #### bulk › screenshots › list List screenshots in a bulk job | Option | Description | | --- | --- | | `-h, --help` | display help for command | ```bash $ bytekit bulk screenshots list ``` ### bulk › create Create a bulk screenshots job | Option | Description | | --- | --- | | `--file ` | Path to newline-delimited file of URLs (plain URL or \{"url":"..."\} per line) | | `--webhook-url ` | HTTPS webhook URL notified when the job completes | | `-h, --help` | display help for command | ```bash $ bytekit bulk create --file --webhook-url ``` ### bulk › get Get a bulk job status | Option | Description | | --- | --- | | `-h, --help` | display help for command | ```bash $ bytekit bulk get ``` ### bulk › cancel Cancel an in-flight bulk job | Option | Description | | --- | --- | | `-h, --help` | display help for command | ```bash $ bytekit bulk cancel ``` --- URL: https://bytekit.com/docs/cli/fetch # Fetch Fetch URL content operations Fetch URL content operations ### fetch › bulk Bulk fetch operations | Option | Description | | --- | --- | | `-h, --help` | display help for command | #### fetch › bulk › create Create a bulk fetch job | Option | Description | | --- | --- | | `--file ` | Path to newline-delimited file of URLs (plain URL or \{"url":"..."\} per line) | | `--webhook-url ` | HTTPS webhook URL notified when the job completes | | `-h, --help` | display help for command | ```bash $ bytekit fetch bulk create --file --webhook-url ``` #### fetch › bulk › get Get a bulk fetch job | Option | Description | | --- | --- | | `-h, --help` | display help for command | ```bash $ bytekit fetch bulk get ``` ### fetch › create Fetch URL content (POST) | Option | Description | | --- | --- | | `--url ` | URL to fetch | | `--format ` | Response format (markdown or html) | | `--country ` | Two-letter country code to route the request through (e.g. us, de) | | `--timeout-ms ` | Per-request timeout in milliseconds | | `--cache-ttl ` | Cache freshness: 0, Nh (hours), or Nd (days), e.g. 48h, 2d, 0 (omitted → server default) | | `--markdown-mode ` | Markdown mode: article, raw, llm | | `-o, --output ` | Write the fetched body bytes to the given file path | | `-h, --help` | display help for command | ```bash $ bytekit fetch create --url --format --country --timeout-ms --cache-ttl --markdown-mode ``` ### fetch › get Fetch URL content (GET via query params) | Option | Description | | --- | --- | | `--url ` | URL to fetch | | `--format ` | Response format (markdown or html) | | `--country ` | Two-letter country code to route the request through (e.g. us, de) | | `--timeout-ms ` | Per-request timeout in milliseconds | | `--cache-ttl ` | Cache freshness: 0, Nh (hours), or Nd (days), e.g. 48h, 2d, 0 (omitted → server default) | | `-o, --output ` | Write the fetched body bytes to the given file path | | `-h, --help` | display help for command | ```bash $ bytekit fetch get --url --format --country --timeout-ms --cache-ttl ``` --- URL: https://bytekit.com/docs/cli # Install & Authenticate Install the ByteKit CLI, authenticate, and learn the global flags shared by every command. The `bytekit` CLI wraps the ByteKit API as shell subcommands — scrape, screenshot, record, monitor, and more, straight from your terminal. ## Install Install globally from npm: ```bash npm install -g @hunt-labs/bytekit-cli ``` This puts the `bytekit` binary on your `PATH`. Verify the install: ```bash bytekit --version ``` ## Authenticate Every command needs an API key. You can provide it two ways: **1. Environment variable (recommended)** Set `BYTEKIT_API_KEY` once and every command picks it up automatically: ```bash export BYTEKIT_API_KEY="sk_live_your_api_key_here" bytekit scrape create --url https://example.com ``` **2. The `--key` flag** Pass the key inline for a single command (overrides the env var): ```bash bytekit scrape create --url https://example.com --key sk_live_your_api_key_here ``` The resolution order is: `--key` flag → `BYTEKIT_API_KEY` environment variable. If neither is set, the command exits with `Error: API key required. Pass --key or set BYTEKIT_API_KEY.` ## Global flags These flags are available on every command: | Flag | Description | | --- | --- | | `--key ` | API key for this invocation. Overrides `BYTEKIT_API_KEY`. | | `--base-url ` | Override the API base URL (or set `BYTEKIT_BASE_URL`). Useful for self-hosted or staging endpoints. | | `--json` | Print the raw JSON response instead of the formatted, human-readable output. | | `-V, --version` | Print the CLI version and exit. | | `-h, --help` | Show help for the program or a subcommand. | For example, to point at a staging endpoint and get raw JSON: ```bash bytekit scrape create --url https://example.com --base-url https://api-stg.bytekit.com --json ``` ## Next steps - [Scrape](https://bytekit.com/docs/cli/scrape) — capture URL content as markdown, HTML, or structured data. - [Screenshots](https://bytekit.com/docs/cli/screenshots) — render full-page or viewport screenshots. - [Monitors](https://bytekit.com/docs/cli/monitors) — track page changes on a schedule. - [API Reference](https://bytekit.com/docs/api) — the underlying REST endpoints. --- URL: https://bytekit.com/docs/cli/monitors # Monitors Page-change monitor operations Page-change monitor operations First-capture behavior is the same for both monitor types: the first capture is a silent baseline (`has_change: false`, `change_pct: null`) with no `change_detected` webhook, whether the monitor is `scrape` or `screenshot`. From the second capture onward both types diff against the previous capture. ### monitors › captures Monitor capture sub-resources | Option | Description | | --- | --- | | `-h, --help` | display help for command | #### monitors › captures › list List captures for a monitor | Option | Description | | --- | --- | | `-h, --help` | display help for command | ```bash $ bytekit monitors captures list ``` ### monitors › create Create a page-change monitor | Option | Description | | --- | --- | | `--url ` | URL to monitor | | `--interval-type ` | Check interval: hourly, daily, weekly, or cron | | `--webhook-url ` | HTTPS webhook URL notified on change | | `--cron ` | Cron expression (required when --interval-type is cron, e.g. "0 * * * *") | | `--type ` | Monitor type: screenshot, scrape (omitted → server default screenshot) | | `--change-threshold ` | Change percentage that triggers a notification (float; omitted → server default 5) | | `--notify-on ` | When to notify: change, every (omitted → server default change) | | `--webhook-secret ` | HMAC secret for webhook signature verification | | `--webhook-headers ` | Extra webhook headers as a JSON object, e.g. '\{"X-Sig":"abc"\}' (omitted → none) | | `--options ` | Screenshot-monitor options as a JSON object (omitted → server defaults) | | `--scrape-options ` | Scrape-monitor options as a JSON object (type=scrape only; omitted → server defaults) | | `-h, --help` | display help for command | ```bash $ bytekit monitors create --url --interval-type --webhook-url --cron --type --change-threshold --notify-on --webhook-secret --webhook-headers --options --scrape-options ``` ### monitors › list List all monitors | Option | Description | | --- | --- | | `--limit ` | Maximum number of monitors to return | | `--cursor ` | Pagination cursor from a previous page | | `--status ` | Filter by status: active, processing, paused, cancelled, suspended (default active) | | `-h, --help` | display help for command | ```bash $ bytekit monitors list --limit --cursor --status ``` ### monitors › get Get a monitor by ID | Option | Description | | --- | --- | | `-h, --help` | display help for command | ```bash $ bytekit monitors get ``` ### monitors › update Update a monitor (including pause/resume via --status) | Option | Description | | --- | --- | | `--interval-type ` | New check interval: hourly, daily, weekly, or cron | | `--cron ` | New cron expression (when interval-type is cron, e.g. "0 * * * *") | | `--webhook-url ` | New HTTPS webhook URL | | `--status ` | Pause or resume the monitor: active, paused | | `--change-threshold ` | New change percentage that triggers a notification (float) | | `--notify-on ` | When to notify: change, every | | `--webhook-secret ` | New HMAC secret for webhook signature verification | | `--webhook-headers ` | New webhook headers as a JSON object, e.g. '\{"X-Sig":"abc"\}' | | `--options ` | New screenshot-monitor options as a JSON object | | `--scrape-options ` | New scrape-monitor options as a JSON object | | `-h, --help` | display help for command | ```bash $ bytekit monitors update --interval-type --cron --webhook-url --status --change-threshold --notify-on --webhook-secret --webhook-headers --options --scrape-options ``` ### monitors › delete Delete a monitor | Option | Description | | --- | --- | | `-h, --help` | display help for command | ```bash $ bytekit monitors delete ``` --- URL: https://bytekit.com/docs/cli/scrape # Scrape Scrape URL content Scrape URL content ### scrape › bulk Bulk scrape operations | Option | Description | | --- | --- | | `-h, --help` | display help for command | #### scrape › bulk › create Create a bulk scrape job | Option | Description | | --- | --- | | `--file ` | Path to newline-delimited file of URLs (plain URL or \{"url":"..."\} per line) | | `--webhook-url ` | HTTPS webhook URL notified when the job completes | | `-h, --help` | display help for command | ```bash $ bytekit scrape bulk create --file --webhook-url ``` #### scrape › bulk › get Get a bulk scrape job | Option | Description | | --- | --- | | `-h, --help` | display help for command | ```bash $ bytekit scrape bulk get ``` ### scrape › create Create a scrape job | Option | Description | | --- | --- | | `--url ` | URL to scrape | | `--format ` | Output format; repeat or comma-separate for multiple (e.g. --format raw_html --format markdown, or --format raw_html,markdown). Sent as the formats array; omitted → server default. | | `--country ` | Two-letter country code to route the request through (e.g. us, de) | | `--timeout-ms ` | Per-request timeout in milliseconds | | `--markdown-mode ` | Markdown extraction mode: article, raw, llm | | `--markdown-query ` | BM25 query string for relevance-ranked markdown filtering (omitted → disabled) | | `--cache-ttl ` | Cache freshness: 0, Nh (hours), or Nd (days), e.g. 48h, 2d, 0 (omitted → server default) | | `--cookies ` | Cookies as a JSON array, e.g. '[\{"name":"a","value":"b"\}]' (omitted → none) | | `--headers ` | Extra request headers as a JSON object, e.g. '\{"X-A":"b"\}' (omitted → none) | | `--delay-ms ` | Post-load delay in milliseconds before capture | | `--events ` | Webhook events (comma-separated): queued, completed, failed (omitted → server default) | | `--token-encoding ` | Tokenizer for --token-budget: cl100k_base, o200k_base (omitted → server default) | | `--custom ` | User payload echoed on the envelope as a JSON object, e.g. '\{"job":"1"\}' (omitted → none) | | `--remove-base64-images` | Strip base64 data: images before conversion (server default) | | `--no-remove-base64-images` | Preserve base64 data: images in the pipeline output | | `--markdown-links ` | Link rendering: inline, references, none, text | | `--markdown-images ` | Image retention: inline, references, none, text | | `--with-links-summary` | Append a Links footer to the markdown output | | `--with-images-summary` | Append an Images footer to the markdown output | | `--markdown-compact` | Collapse excessive whitespace in the markdown output | | `--markdown-filter-images` | Filter low-signal images from the markdown output | | `--markdown-include-media` | Return rich link/image objects and a tables array | | `--markdown-include-warnings` | Include markdown-pipeline and tag-filter warnings | | `--markdown-include-stats` | Include a top-level stats object (chars, tokens, blocks) | | `--async` | Queue the scrape and return a job envelope immediately (no wait) | | `--webhook-url ` | HTTPS webhook URL notified when an async scrape completes | | `--token-budget ` | Maximum tokens of markdown to return | | `--clean-markdown` | Post-process markdown with the LLM cleaner (billed 3x) | | `--include-tags ` | Comma-separated HTML tags to keep (e.g. article,main) | | `--exclude-tags ` | Comma-separated HTML tags to drop (e.g. nav,footer) | | `--mobile` | Render with a mobile viewport/user-agent | | `--raw` | Print the requested format body raw, with no JSON wrapper | | `-o, --output ` | Write the output to the given file path (implies --raw unless --json is set) | | `-h, --help` | display help for command | ```bash $ bytekit scrape create --url --format --country --timeout-ms --markdown-mode --markdown-query --cache-ttl --cookies --headers --delay-ms --events --token-encoding --custom --remove-base64-images --no-remove-base64-images --markdown-links --markdown-images --with-links-summary --with-images-summary --markdown-compact --markdown-filter-images --markdown-include-media --markdown-include-warnings --markdown-include-stats --async --webhook-url --token-budget --clean-markdown --include-tags --exclude-tags --mobile --raw ``` ### scrape › get Get a scrape job by ID (use --raw/-o/--format to retrieve a completed body) | Option | Description | | --- | --- | | `--raw` | Print the requested format body raw, with no JSON wrapper | | `-o, --output ` | Write the output to the given file path (implies --raw unless --json is set) | | `--format ` | Which persisted format to emit — only takes effect with --raw/-o; ignored otherwise | | `-h, --help` | display help for command | ```bash $ bytekit scrape get --raw --format ``` --- URL: https://bytekit.com/docs/cli/screenshots # Screenshots Screenshot operations Screenshot operations ### screenshots › create Capture a screenshot | Option | Description | | --- | --- | | `--url ` | URL to screenshot | | `--full-page` | Capture the full page (default: viewport only) | | `--device ` | Device profile: desktop, mobile | | `--viewport ` | Viewport size WIDTHxHEIGHT (e.g. 1280x720) | | `--format ` | Image format: jpeg, png | | `--quality ` | JPEG quality (1-100) | | `--wait-until ` | Wait condition: load, domcontentloaded, networkidle | | `--clip ` | Clip region X,Y,WIDTH,HEIGHT (e.g. 0,0,800,600) | | `--block-ads` | Block ads before capture (server default) | | `--no-block-ads` | Do not block ads | | `--block-cookie-banners` | Block cookie-consent banners before capture (server default) | | `--no-block-cookie-banners` | Do not block cookie-consent banners | | `--scroll` | Scroll the page before capture to trigger lazy content (server default) | | `--no-scroll` | Do not scroll the page before capture | | `--wait-for-selector ` | Wait until this CSS selector is present before capture | | `--delay-ms ` | Post-load delay in milliseconds before capture | | `--dark-mode` | Emulate prefers-color-scheme: dark | | `--device-scale-factor ` | Device pixel ratio: 1, 2 | | `--include-html` | Include the rendered HTML alongside the screenshot | | `--country ` | Two-letter country code to route the request through (e.g. us, de) | | `--language ` | Accept-Language override (e.g. en-US) | | `--async` | Queue the screenshot and return a job envelope immediately (no wait) | | `-o, --output ` | Download the captured image to this file path (use - for stdout). Not valid with --json. | | `-h, --help` | display help for command | ```bash $ bytekit screenshots create --url --full-page --device --viewport --format --quality --wait-until --clip --block-ads --no-block-ads --block-cookie-banners --no-block-cookie-banners --scroll --no-scroll --wait-for-selector --delay-ms --dark-mode --device-scale-factor --include-html --country --language --async ``` ### screenshots › get Get a screenshot by ID | Option | Description | | --- | --- | | `-o, --output ` | Download the captured image to this file path (use - for stdout). Not valid with --json. | | `-h, --help` | display help for command | ```bash $ bytekit screenshots get ``` --- URL: https://bytekit.com/docs/cli/search # Search Run a web search and return ranked results Run a web search and return ranked results ### search Run a web search and return ranked results | Option | Description | | --- | --- | | `--type ` | Search index to query: web (default), news, or images | | `--limit ` | Maximum number of results to return (1–100) | | `--country ` | Two-letter country code to localize results (e.g. us, de) | | `--language ` | Two-letter language code for results (e.g. en, fr) | | `--date-range ` | Restrict results to a recency window: any (default), hour, day, week, month, or year | | `-h, --help` | display help for command | ```bash $ bytekit search --type --limit --country --language --date-range ``` --- URL: https://bytekit.com/docs/cli/sitemap # Sitemap Sitemap crawl operations Sitemap crawl operations ### sitemap › create Start a sitemap crawl | Option | Description | | --- | --- | | `--url ` | Root URL to crawl | | `--strategy ` | Crawl strategy: quick, standard, deep (omitted → server default standard) | | `--max-depth ` | Maximum crawl depth 1-5 (omitted → server default 2) | | `--max-urls ` | Maximum URLs to collect (omitted → server default 5000) | | `-h, --help` | display help for command | ```bash $ bytekit sitemap create --url --strategy --max-depth --max-urls ``` ### sitemap › get Get a sitemap job by ID | Option | Description | | --- | --- | | `-h, --help` | display help for command | ```bash $ bytekit sitemap get ``` --- URL: https://bytekit.com/docs/cli/usage # Usage Account usage and billing-period reporting Account usage and billing-period reporting ### usage › get Get current billing-period usage summary | Option | Description | | --- | --- | | `-h, --help` | display help for command | ```bash $ bytekit usage get ``` ### usage › daily Get daily usage breakdown (default last 30 days; max 366) | Option | Description | | --- | --- | | `--from ` | Start date YYYY-MM-DD (omitted → server default, 30 days ago) | | `--to ` | End date YYYY-MM-DD (omitted → server default, today) | | `--api-key ` | Filter by API key prefixed ID (omitted → all keys) | | `-h, --help` | display help for command | ```bash $ bytekit usage daily --from --to --api-key ``` ### usage › by-endpoint Get per-endpoint bandwidth breakdown (default last 30 days; max 366) | Option | Description | | --- | --- | | `--from ` | Start date YYYY-MM-DD (omitted → server default, 30 days ago) | | `--to ` | End date YYYY-MM-DD (omitted → server default, today) | | `--api-key ` | Filter by API key prefixed ID (omitted → all keys) | | `-h, --help` | display help for command | ```bash $ bytekit usage by-endpoint --from --to --api-key ``` --- URL: https://bytekit.com/docs/cli/webhook-deliveries # Webhook-deliveries Webhook delivery operations Webhook delivery operations ### webhook-deliveries › list List webhook deliveries | Option | Description | | --- | --- | | `--limit ` | Maximum number of deliveries to return | | `--cursor ` | Pagination cursor from a previous page | | `--status ` | Filter by delivery status: pending, delivered, failed, exhausted | | `--since ` | Only deliveries created after this ISO date-time | | `-h, --help` | display help for command | ```bash $ bytekit webhook-deliveries list --limit --cursor --status --since ``` ### webhook-deliveries › retry Requeue a failed or exhausted webhook delivery | Option | Description | | --- | --- | | `-h, --help` | display help for command | ```bash $ bytekit webhook-deliveries retry ``` --- URL: https://bytekit.com/docs/guides/authentication # Authentication How to authenticate with the ByteKit API using API keys. ByteKit authenticates requests with Bearer API keys. Every request to a capture or data endpoint must include an `Authorization` header. ## API key format Keys are prefixed `sk_live_` so they are easy to identify in logs and config. Treat the full value as a secret. ## Sending the Bearer header Include the key in every request: ```bash curl -X POST https://api.bytekit.com/v1/scrape \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com"}' ``` The key is stored as a SHA-256 hash on the server. The plaintext value is only ever visible once — at creation time. ## Managing API keys Create, rename, and revoke API keys from the [dashboard](https://app.bytekit.com). Each account can hold up to **50 active keys**, with per-key billing attribution so you can split usage cleanly across services. The plaintext key is shown **once**, at creation time — copy it immediately and store it as a secret. Revoke a key from the dashboard to free a slot; revoked keys do not count toward the limit. ## Next steps - [Quickstart](https://bytekit.com/docs/quickstart) — make your first API call - [API Reference: Account](https://bytekit.com/docs/api/account/getaccount) — read your account, plan, and usage --- URL: https://bytekit.com/docs/guides/errors # Errors ByteKit error response shape, common error codes, and retry guidance. All errors from the ByteKit API follow a consistent envelope shape so you can handle them programmatically. ## Error response shape Every `4xx`/`5xx` response across the API uses one unified envelope. `status` is always the literal string `"failed"`, so you can branch on the response body alone. ```json { "status": "failed", "error": { "code": "rate_limited", "message": "Too many requests. Retry after 2 seconds.", "httpStatus": 429, "failedAt": "2026-06-16T19:38:02.084Z", "details": {} } } ``` | Field | Description | |-------|-------------| | `status` | Always `"failed"` on an error response — the top-level discriminator | | `error.code` | Machine-readable string identifier | | `error.message` | Human-readable explanation | | `error.httpStatus` | Mirrors the HTTP response status (useful for logged or replayed bodies) | | `error.failedAt` | ISO-8601 UTC timestamp of when the error was produced | | `error.details` | Optional object with additional context (e.g. which field failed validation) | ## Common error codes | HTTP status | Code | Meaning | |-------------|------|---------| | `400` | `invalid_url` | A supplied URL (including `webhook_url`) is malformed or uses an unsupported scheme | | `401` | `unauthorized` | Missing or malformed `Authorization` header | | `401` | `invalid_api_key` | API key is present but not valid | | `402` | `quota_exceeded` | Account has exceeded its plan quota | | `403` | `forbidden` | Key is valid but lacks permission for this action | | `422` | `validation_error` | Malformed JSON or a request field failed validation | | `429` | `rate_limited` | Too many requests; back off and retry | | `500` | `internal_error` | The upstream fetch or browser render failed | ## Webhook URLs Must Use HTTPS All webhook URLs submitted to the ByteKit API must use HTTPS. Plain HTTP URLs (e.g. `http://example.com/webhook`) are rejected with a `400` `invalid_url` error. **Why HTTPS only?** Webhook payloads may contain sensitive data (API keys, session tokens, scrape results). HTTPS encryption protects this data in transit. **Request with an invalid webhook URL:** ```json { "webhook_url": "http://example.com/webhook" } ``` **Response (HTTP 400)** — the same unified envelope as every other error; `error.details` names the offending field and reason: ```json { "status": "failed", "error": { "code": "invalid_url", "message": "url is not a valid HTTP(S) URL: scheme_unsupported", "httpStatus": 400, "failedAt": "2026-06-16T19:38:02.340Z", "details": { "url": "http://example.com/webhook", "field": "webhook_url", "reason": "scheme_unsupported" } } } ``` **Working example:** ```json { "webhook_url": "https://example.com/webhook" } ``` This applies to all endpoints that accept a `webhook_url` parameter: `/v1/scrape` (async), `/v1/scrape/bulk`, `/v1/fetch/bulk`, `/v1/bulk`, `/v1/monitors`, and `/v1/sitemap`. ## Retry guidance Not all errors are retryable. Use this table to decide: | Code | Retry? | Guidance | |------|--------|---------| | `invalid_url` | No | Fix the URL before retrying | | `validation_error` | No | Fix the request body before retrying | | `unauthorized` / `invalid_api_key` | No | Check your API key | | `quota_exceeded` | No | Upgrade your plan or wait for quota reset | | `forbidden` | No | You do not have access to this resource | | `rate_limited` | Yes | Exponential backoff; honour the `Retry-After` header | | `internal_error` | Yes | Exponential backoff; most resolve on retry | For retriable errors, start with a 1-second delay and double on each subsequent failure, up to a maximum of 60 seconds. Respect the `Retry-After` header value when it is present on `429` responses. ## Example: handling errors in curl ```bash response=$(curl -s -w "\n%{http_code}" -X POST https://api.bytekit.com/v1/scrape \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com"}') http_code=$(echo "$response" | tail -1) # Body is everything except the trailing status-code line — drop only the last # line so multi-line JSON responses survive intact. body=$(echo "$response" | sed '$d') if [ "$http_code" -ge 400 ]; then echo "Error $http_code: $body" fi ``` ## Next steps - [Rate Limits](https://bytekit.com/docs/guides/rate-limits) — quota model, concurrency slots, rate limit headers - [Scraping](https://bytekit.com/docs/guides/scraping) — when `500 internal_error` occurs and how fallback works - [API Reference](https://bytekit.com/docs/api) — per-endpoint error codes --- URL: https://bytekit.com/docs/guides/monitors # Monitors Detect page changes on any URL — by screenshot or scrape — and receive webhook notifications. Monitors periodically capture a URL and notify you when the page changes. Each monitor uses one of two capture types: `screenshot` (perceptual/visual change detection) or `scrape` (content and markdown change detection). Use them for price tracking, content alerts, uptime monitoring, or competitor surveillance. ## Create a monitor ```bash curl -X POST https://api.bytekit.com/v1/monitors \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/pricing", "interval_type": "hourly", "webhook_url": "https://your-server.example.com/webhooks/bytekit" }' ``` | Field | Description | |-------|-------------| | `url` | The page to monitor (required) | | `interval_type` | Check frequency — one of `hourly`, `daily`, `weekly`, or `cron` (required) | | `webhook_url` | HTTPS webhook endpoint to receive notifications. `http://` is rejected with a 400 `invalid_url` (reason `scheme_unsupported`) (required) | | `cron_expression` | 5-field cron expression. Required when `interval_type` is `cron` | | `type` | Capture mode — `screenshot` (default) or `scrape` | | `change_threshold` | Percentage of change required to trigger a notification (0–100, default `5`). Screenshot monitors only | | `notify_on` | `change` (default — notify only when content changed) or `every` (notify on every capture) | | `webhook_secret` | Optional secret used to sign webhook deliveries | | `webhook_headers` | Optional custom headers (max 20) to include on each webhook delivery | A successful response returns the monitor object with an `id` prefixed `mon_`: ```json { "id": "mon_...", "type": "screenshot", "url": "https://example.com/pricing", "status": "active", "interval_type": "hourly", "cron_expression": null, "next_capture_at": "2026-01-01T01:00:00Z", "notify_on": "change", "webhook_url": "https://your-server.example.com/webhooks/bytekit", "captures_count": 0, "changes_count": 0, "created_at": "2026-01-01T00:00:00Z" } ``` To monitor on a schedule, set `interval_type` to `cron` and supply a 5-field `cron_expression`: ```bash curl -X POST https://api.bytekit.com/v1/monitors \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/pricing", "interval_type": "cron", "cron_expression": "0 9 * * 1-5", "webhook_url": "https://your-server.example.com/webhooks/bytekit" }' ``` ## List monitors ```bash curl https://api.bytekit.com/v1/monitors \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" ``` ## Get a monitor ```bash curl https://api.bytekit.com/v1/monitors/mon_... \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" ``` ## Update a monitor ```bash curl -X PATCH https://api.bytekit.com/v1/monitors/mon_... \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{"interval_type": "daily"}' ``` You can update any subset of the monitor's mutable fields — for example `interval_type`, `cron_expression`, `webhook_url`, `notify_on`, `change_threshold`, or `status` (`active` / `paused`). Switching to a `cron` schedule requires a `cron_expression`: ```bash curl -X PATCH https://api.bytekit.com/v1/monitors/mon_... \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{"interval_type": "cron", "cron_expression": "*/30 * * * *"}' ``` ## Delete a monitor ```bash curl -X DELETE https://api.bytekit.com/v1/monitors/mon_... \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" ``` ## Monitor captures Each time a monitor fires it records a capture event — on **every** run, whether or not the content changed. Each capture carries a `has_change` flag marking whether the content differed from the previous capture. Webhook delivery is separate: the capture is always recorded, but whether a webhook is *sent* depends on `has_change` and the monitor's `notify_on` setting (see [Webhook payload](#webhook-payload) below). Retrieve the capture history with: ```bash curl https://api.bytekit.com/v1/monitors/mon_.../captures \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" ``` Each capture in the list represents one capture event and includes change metadata — the timestamp, whether the content changed (`has_change`), the percentage of content that changed (`change_pct`), and the perceptual hash distance from the previous capture. Captures record *that* the content changed and by how much; they do not include a line-by-line textual diff. ### First-capture behavior (silent baseline for both types) The very first capture of a monitor has **no previous capture to compare against**. Both monitor types treat that first run as a **silent baseline** — one universal "first capture" behavior, no per-type divergence: | Monitor type | First capture | `has_change` | `change_pct` | Webhook on first capture | |--------------|---------------|--------------|--------------|--------------------------| | `scrape` | Silent baseline | `false` | `null` | **No** `monitor.change_detected`-equivalent webhook (the baseline is just recorded) | | `screenshot` | Silent baseline | `false` | `null` | **No** `monitor.change_detected` webhook (the baseline is just recorded) | Neither a **scrape** nor a **screenshot** monitor alerts you on its first run — both only alert once a *subsequent* capture differs (scrape by exact content-hash inequality, screenshot by at least `change_threshold`). A monitor with `notify_on: "every"` still sends a `monitor.captured` webhook on the first capture — that is the per-capture notification, not a change alert, and it fires for both monitor types. From the second capture onward both types behave the same way each new capture is compared against the previous one — scrape by exact SHA-256 content equality, screenshot by perceptual-hash distance against `change_threshold`. **Existing scrape monitors:** if you created a scrape monitor before this change shipped, its first post-deploy capture will no longer fire a `monitor.change_detected`-equivalent webhook — the first capture after the change is now the silent baseline, matching screenshot monitors. Steady-state alerting (second capture onward) is unaffected. ## Webhook payload When a monitor fires, ByteKit sends a `POST` to your `webhook_url`. Every delivery carries an `X-ByteKit-Event` header naming the event type — dispatch on that header rather than inspecting the body shape. The event type and payload depend on the monitor's `type`. ### Screenshot monitors (`type: "screenshot"`, default) Screenshot captures send `monitor.change_detected` when the page changed (`X-ByteKit-Event: monitor.change_detected`) and `monitor.captured` when there was no change but `notify_on: "every"` is set (`X-ByteKit-Event: monitor.captured`). The body carries the capture metadata: ```json { "monitor_id": "mon_...", "screenshot_id": "ss_...", "has_change": true, "change_pct": 42.0, "hash_distance": 18, "url": "https://example.com/pricing" } ``` | Field | Description | |-------|-------------| | `has_change` | `true` when the captured content differs from the previous capture | | `change_pct` | Percentage of content that changed (0–100) | | `hash_distance` | Perceptual-hash distance from the previous capture | | `screenshot_id` | The screenshot that triggered the notification | ### Scrape monitors (`type: "scrape"`) Scrape captures send `scrape.completed` (`X-ByteKit-Event: scrape.completed`) with the full scrape result envelope inline: ```json { "success": true, "type": "scrape.completed", "id": "mon_...", "scrape_id": "sc_...", "has_change": true, "change_pct": 100.0, "metadata": {}, "scrape": { "status": "success", "id": "sc_...", "url": "https://example.com/pricing", "final_url": "https://example.com/pricing", "content_length": 1256, "status_code": 200, "formats": { "markdown": "..." } } } ``` | Field | Description | |-------|-------------| | `has_change` | `true` when the captured content differs from the previous capture | | `change_pct` | Percentage of content that changed (0–100) | | `scrape` | Full scrape result envelope (same shape as `POST /v1/scrape` response) | Your endpoint must respond with `2xx` within 10 seconds. Failed deliveries are retried with exponential backoff. ## Next steps - [API Reference: Monitors](https://bytekit.com/docs/api/monitors/createmonitor) — full endpoint schema - [Errors](https://bytekit.com/docs/guides/errors) — error codes returned by monitor endpoints --- URL: https://bytekit.com/docs/guides/rate-limits # Rate Limits How ByteKit enforces quotas, concurrency slots, and rate limits. ByteKit enforces three independent limits: **rate limits** (requests per second), **concurrency slots** (simultaneous in-flight requests), and **quota** (monthly usage caps). All three are enforced before any capture work begins. ## Quota model Quota is pre-incremented on every request and corrected after the request completes: 1. **Pre-increment** — your usage counter is incremented by an estimate before the capture starts. This prevents over-committing capacity even for concurrent requests. 2. **Adjust** — once the response returns, the actual `contentLength` (compressed wire bytes) replaces the estimate for bandwidth accounting. 3. **Compensating decrement** — if the request fails with a terminal error, the pre-incremented amount is rolled back so you are not charged for failed requests. This means your dashboard usage counter may briefly read higher than actual consumption during a burst of requests, then settle to the true value. ## Concurrency slots Each account has a concurrency limit — the maximum number of in-flight requests at one time. Slots are released automatically a few minutes after a client disconnects, so a crashed client cannot permanently hold them. When you exceed your concurrency limit, you receive `429 rate_limited` with a `Retry-After` header indicating how long to wait before retrying. ## Rate limit headers Every response includes headers that show your current position: | Header | Description | |--------|-------------| | `X-RateLimit-Limit` | Your account's request-per-second limit | | `X-RateLimit-Remaining` | Requests remaining in the current window | | `X-RateLimit-Reset` | Unix timestamp when the window resets | | `Retry-After` | Seconds to wait before retrying (present on `429` only) | ## Bandwidth metering Bandwidth usage is measured as `contentLength` — the compressed wire bytes returned by the origin server. If the origin sends gzip or Brotli-encoded content, the compressed size is what counts, not the decompressed size. This matches what your client actually receives over the network. ## Handling 429 responses When you receive `429 rate_limited`: 1. Read the `Retry-After` header value (in seconds). 2. Wait at least that long before retrying. 3. If no `Retry-After` is present, use exponential backoff starting at 1 second. ```bash # Check rate limit headers curl -I -X POST https://api.bytekit.com/v1/scrape \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com"}' ``` ## Next steps - [Errors](https://bytekit.com/docs/guides/errors) — full error code reference and retry guidance - [Scraping](https://bytekit.com/docs/guides/scraping) — formats, options, latency, and async polling --- URL: https://bytekit.com/docs/guides/scraping # Scraping Scrape any URL as HTML, markdown, or structured content — formats, options, latency, and async polling. `POST /v1/scrape` fetches a URL and returns its content in the format you ask for. The same request shape works for every site; the options you pass determine the output and how long the call takes. ## Rendering & latency Most scrapes return in well under a second. Some options require ByteKit to fully render the page before it can return content, which takes longer — typically a few seconds. Pass these only when you need them: | Option | When it adds latency | |--------|----------------------| | `cookies` | Non-empty array | | `headers` | Non-empty object | | `delay_ms` | Greater than `0` | | `async` | `true` (request is queued and processed off the fast path) | The `country` option routes the request through a geo-located proxy. On its own it does not add rendering latency; it only does so when combined with one of the options above. Looking for `wait_for_selector` or `wait_until`? Those are not `ScrapeRequest` options — they belong to the [Screenshot](https://bytekit.com/docs/api/screenshots/createscreenshot) and [Recording](https://bytekit.com/docs/api/recordings/createrecording) endpoints. Sending them to `/v1/scrape` returns `422`. ```bash # A simple, low-latency scrape curl -X POST https://api.bytekit.com/v1/scrape \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com"}' ``` ```bash # Slower: delay_ms forces a full render and waits before returning curl -X POST https://api.bytekit.com/v1/scrape \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "delay_ms": 2000 }' ``` ## Response fields The default format is `raw_html` — pass `formats` to request others (`markdown`, `html`, `links`, `images`). The `formats` object in the response is keyed by the format you requested. | Field | Description | |-------|-------------| | `formats.raw_html` | Page content in the requested format (`raw_html` is the default when `formats` is omitted) | | `contentLength` | Compressed wire bytes — used for bandwidth billing | | `statusCode` | HTTP status code of the origin page | | `metadata.title` | Page `` element | The `X-Scrape-*` response headers describe how each request was handled (cache status, timing, and the final URL) — useful for debugging. ## Async scrapes For long-running pages, pass `"async": true` to get a `202 Accepted` response with a scrape `id` prefixed `sc_`. Poll the result with `GET /v1/scrape/{id}`. ```bash # Start async scrape curl -X POST https://api.bytekit.com/v1/scrape \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com", "async": true}' # Poll for result curl https://api.bytekit.com/v1/scrape/sc_... \ -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" ``` ## Webhook event header Every outbound webhook POST includes an `X-ByteKit-Event` header with the event type: | Event type | Trigger | |---|---| | `bulk.completed` | A bulk job finishes (all items processed) | | `scrape.completed` | An async scrape succeeds | | `scrape.failed` | An async scrape fails terminally | | `monitor.change_detected` | A monitor fires and detects a change (screenshot or content) | | `monitor.captured` | A monitor fires with no change detected (notify_on: every) | | `sitemap.completed` | A sitemap job finishes | | `sitemap.failed` | A sitemap job fails terminally | Use this header to dispatch events without inspecting the body shape: ```python from flask import Flask, request app = Flask(__name__) @app.route("/webhooks/bytekit", methods=["POST"]) def handle(): event = request.headers.get("X-ByteKit-Event") if event == "bulk.completed": handle_bulk(request.json) elif event == "scrape.completed": handle_scrape(request.json) return "", 200 ``` `X-ByteKit-Event` is a reserved header — you cannot supply it via `webhook_headers` in monitor or bulk configuration. Attempts to do so will be rejected with a `422` at request time. ## Next steps - [Rate Limits](https://bytekit.com/docs/guides/rate-limits) — quota model and concurrency slots - [Errors](https://bytekit.com/docs/guides/errors) — how to interpret and retry error responses - [API Reference: Scrape](https://bytekit.com/docs/api/scrape/createscrape) — full request/response schema --- URL: https://bytekit.com/docs/libraries/go # Go Call the ByteKit REST API from Go with the standard library. ByteKit is a plain REST API. There is no Go SDK — and you do not need one. The TypeScript and Python SDKs are thin wrappers over the same HTTP endpoints, so calling ByteKit from Go is just a standard `net/http` request. ```go package main import ( "bytes" "fmt" "io" "net/http" ) func main() { body := bytes.NewBufferString(`{"url":"https://example.com","formats":["markdown"]}`) req, _ := http.NewRequest("POST", "https://api.bytekit.com/v1/scrape", body) req.Header.Set("Authorization", "Bearer sk_live_your_api_key_here") req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() out, _ := io.ReadAll(resp.Body) fmt.Println(string(out)) } ``` ## Next steps - [API Reference](https://bytekit.com/docs/api) — every endpoint, request, and response. - [TypeScript SDK](https://bytekit.com/docs/libraries/typescript) and [Python SDK](https://bytekit.com/docs/libraries/python) — typed clients over the same REST API. --- URL: https://bytekit.com/docs/libraries/python # Python SDK Official Python client for ByteKit, with sync and async support. The official Python SDK is generated from the ByteKit OpenAPI spec, so its models and methods always match the API. Every operation ships in both synchronous and asynchronous form. ## Install ```bash pip install bytekit-sdk ``` The PyPI distribution is `bytekit-sdk`; the import name is `bytekit`. ## Your first scrape ```python from bytekit import AuthenticatedClient from bytekit.api.scrape import create_scrape from bytekit.models.scrape_request import ScrapeRequest from bytekit.models.scrape_request_formats_item import ScrapeRequestFormatsItem from bytekit.models.scrape_success_envelope import ScrapeSuccessEnvelope client = AuthenticatedClient( base_url="https://api.bytekit.com", token="sk_live_your_api_key_here", ) # Request the markdown format explicitly — otherwise response.formats.markdown is UNSET. body = ScrapeRequest( url="https://example.com", formats=[ScrapeRequestFormatsItem.MARKDOWN], ) response = create_scrape.sync(client=client, body=body) # .sync() returns None on a transport error, and a queued/error envelope on the # async or failure paths. Only a ScrapeSuccessEnvelope carries populated formats. if response is None: print("Request failed — no response returned.") elif isinstance(response, ScrapeSuccessEnvelope) and isinstance(response.formats.markdown, str): print(response.formats.markdown) else: # Queued (ScrapeQueuedEnvelope) or error (ScrapeErrorEnvelope) — inspect `response`. print(f"Scrape not complete: {response}") ``` Each operation exposes four call styles: - `create_scrape.sync(...)` — returns the parsed model (or `None`). - `create_scrape.sync_detailed(...)` — returns the full `Response` (status code, headers, body). - `create_scrape.asyncio(...)` — awaitable form of `sync`. - `create_scrape.asyncio_detailed(...)` — awaitable form of `sync_detailed`. ## Async usage ```python import asyncio from bytekit import AuthenticatedClient from bytekit.api.screenshots import create_screenshot from bytekit.models.screenshot_request import ScreenshotRequest async def main(): client = AuthenticatedClient( base_url="https://api.bytekit.com", token="sk_live_your_api_key_here", ) body = ScreenshotRequest(url="https://example.com") response = await create_screenshot.asyncio(client=client, body=body) print(response) asyncio.run(main()) ``` ## What's available Operations are grouped by capability under `bytekit.api.*`: `scrape`, `scrape_bulk`, `screenshots`, `recordings`, `bulk`, `fetch`, `fetch_bulk`, `monitors`, `sitemap`, and `account`. See the [Python SDK Reference](https://bytekit.com/docs/sdk/python/client) for every module, function, and model. ## Next steps - [TypeScript SDK](https://bytekit.com/docs/libraries/typescript) — the same API surface in TypeScript. - [API Reference](https://bytekit.com/docs/api) — the underlying REST endpoints. - [Errors](https://bytekit.com/docs/guides/errors) — error codes and retry guidance. --- URL: https://bytekit.com/docs/libraries/typescript # TypeScript SDK Type-safe ByteKit client for Node.js and modern JavaScript runtimes. The official TypeScript SDK is a thin, type-safe wrapper over the ByteKit REST API. It works in Node.js and any runtime with a global `fetch` (Bun, Deno, Cloudflare Workers, modern browsers). ## Install ```bash npm install @hunt-labs/bytekit-sdk ``` ## Initialize ```ts import { ByteKit } from '@hunt-labs/bytekit-sdk'; const client = new ByteKit({ apiKey: process.env.BYTEKIT_API_KEY!, // your sk_live_… key }); ``` ## Your first scrape ```ts const result = await client.scrape.create({ url: 'https://example.com', formats: ['markdown'], }); console.log(result); ``` ## Capture a screenshot ```ts const shot = await client.screenshots.create({ url: 'https://example.com', full_page: true, }); console.log(shot); ``` ## Handle errors Any 4xx/5xx response throws a `ByteKitError` carrying the HTTP `status` and the API `error.code`: ```ts import { ByteKit, ByteKitError } from '@hunt-labs/bytekit-sdk'; try { await client.scrape.create({ url: 'https://example.com' }); } catch (err) { if (err instanceof ByteKitError) { console.error(`${err.status} ${err.code}: ${err.message}`); } else { throw err; } } ``` ## Async scrapes Pass `async: true` to enqueue the job and poll by ID: ```ts const queued = await client.scrape.create({ url: 'https://example.com', async: true }); // later — poll with the returned sc_… id const final = await client.scrape.get('sc_...'); ``` ## What's available The client exposes one resource per capability: `scrape`, `screenshots`, `bulk`, `fetch`, `monitors`, `sitemap`, and `account` (each with `create` / `get` / `list` methods as applicable, plus nested resources like `scrape.bulk` and `monitors.captures`). See the [TypeScript SDK Reference](https://bytekit.com/docs/sdk/typescript/client) for every method and option. ## Next steps - [Python SDK](https://bytekit.com/docs/libraries/python) — the same API surface in Python. - [API Reference](https://bytekit.com/docs/api) — the underlying REST endpoints. - [Errors](https://bytekit.com/docs/guides/errors) — error codes and retry guidance. --- URL: https://bytekit.com/docs/api/account/getaccount # Get account details Returns account, plan, and current API key details, including the `api_key` and `subscription` fields. **GET** `/v1/account` ## Responses ### 200 — Account, plan, and current API key details. - `id` - `email` - `name` - `plan` - `api_key` - `subscription` - `auto_topup` - `cancelled_at` - `grace_period_expires_at` - `created_at` - `owner_user_id` - `notifications` - `terms_version` - `has_activity` --- URL: https://bytekit.com/docs/api/account # Account Account API — endpoints, requests, and responses. The **Account** API. 1 endpoint: - [Get account details](https://bytekit.com/docs/api/account/getaccount) — GET /v1/account --- URL: https://bytekit.com/docs/api/bulk/createbulk # Create mixed bulk job Enqueues screenshots or scrapes in a single batch. Provide `urls` (simple list) or `items` (per-item config), not both. Webhook deliveries include `X-ByteKit-Event: bulk.completed` so receivers can dispatch on the header without parsing the body shape. **POST** `/v1/bulk` ## Request Body - `urls` - `items` - `defaults` - `webhook_url` (required) - `webhook_secret` - `metadata` ## Responses ### 202 — Bulk job accepted. - `id` - `status` - `total` - `completed` - `failed` - `created_at` - `completed_at` - `total_credits_charged` - `estimated_credits` - `webhook_url` ### 400 — Bad request. When a `url` (or item URL) fails strict validation the error code is `invalid_url` and the `details` object contains: - `field` — the request field that failed (e.g. `"url"`) - `url` — the rejected value - `reason` — one of: `scheme_missing`, `scheme_unsupported`, `scheme_separator_typo`, `host_empty`, `host_invalid`, `host_blocked_private`, `host_blocked_loopback`, `host_too_long`, `host_unicode_not_punycode`, `userinfo_present`, `url_too_long`, `control_chars`, `not_a_url` - `status` - `error` ### 422 — Validation error. `bulk_too_large` is returned when the item count exceeds the account plan's `max_bulk_size` cap (also surfaced as `max_bulk_size` on `GET /v1/account`). - `status` - `error` --- URL: https://bytekit.com/docs/api/bulk/deletebulk # Cancel a bulk job Cancels an in-flight bulk job: drains its still-queued (pending) child jobs and refunds the quota held for exactly those drained children. Children already completed, failed, or in progress are untouched and keep their charge. Idempotent — cancelling an already-terminal job (completed, failed, or cancelled) is a no-op that returns 200 with the job's real, unchanged status and refunds nothing. **DELETE** `/v1/bulk/{id}` ## Parameters - `id` (path, string, required) ## Responses ### 200 — Bulk job after the cancel attempt. `status` is `cancelled` when the job was in flight, or its real terminal status when it had already finished. - `id` - `status` - `total` - `completed` - `failed` - `created_at` - `completed_at` - `total_credits_charged` - `estimated_credits` - `webhook_url` ### 404 — Resource not found. - `status` - `error` --- URL: https://bytekit.com/docs/api/bulk/getbulk # Get bulk job status **GET** `/v1/bulk/{id}` ## Parameters - `id` (path, string, required) ## Responses ### 200 — Successful response. - `id` - `status` - `total` - `completed` - `failed` - `created_at` - `completed_at` - `total_credits_charged` - `estimated_credits` - `webhook_url` ### 404 — Resource not found. - `status` - `error` --- URL: https://bytekit.com/docs/api/bulk # Bulk Bulk API — endpoints, requests, and responses. The **Bulk** API. 5 endpoints: - [List in-flight bulk jobs](https://bytekit.com/docs/api/bulk/listbulk) — GET /v1/bulk - [Create mixed bulk job](https://bytekit.com/docs/api/bulk/createbulk) — POST /v1/bulk - [Get bulk job status](https://bytekit.com/docs/api/bulk/getbulk) — GET /v1/bulk/:id - [Cancel a bulk job](https://bytekit.com/docs/api/bulk/deletebulk) — DELETE /v1/bulk/:id - [List bulk job items](https://bytekit.com/docs/api/bulk/listbulkscreenshots) — GET /v1/bulk/:id/screenshots --- URL: https://bytekit.com/docs/api/bulk/listbulk # List in-flight bulk jobs Returns the caller's bulk jobs, most recent first. Optionally filter to a single status; when omitted, jobs of every status are returned. **GET** `/v1/bulk` ## Parameters - `status` (query, string) ## Responses ### 200 — Successful response. - `id` - `status` - `total` - `completed` - `failed` - `created_at` - `completed_at` - `total_credits_charged` - `estimated_credits` - `webhook_url` ### 422 — Validation error. Returned when `status` is not one of the documented values. - `status` - `error` --- URL: https://bytekit.com/docs/api/bulk/listbulkscreenshots # List bulk job items **GET** `/v1/bulk/{id}/screenshots` ## Parameters - `id` (path, string, required) ## Responses ### 200 — Successful response. --- URL: https://bytekit.com/docs/api/fetch/getfetch # Fetch a URL (GET) Direct HTTP fetch. The response body IS the content; metadata is returned in X-Fetch-* headers. Omit `format` for raw passthrough. Use `format=markdown` or `format=html` for conversion. **GET** `/v1/fetch` ## Parameters - `url` (query, string, required) - `format` (query, string) - `country` (query, string) - `timeout_ms` (query, integer) - `cache_ttl` (query) — How long a freshly fetched URL may be served from cache. `0` skips the cache read; cache-eligible fresh results are still written with the default 7-day TTL so later non-zero-TTL callers can HIT. Safety-classified WAF husks remain live-only and are not written. Accepts `Nh` (hours, up to 168h), `Nd` (days, up to 7d), or the integer `0`. Examples: `0`, `48h`, `2d`. Invalid values receive a 422 validation_error with an actionable message. ## Responses ### 200 — Content from upstream. ### 400 — Invalid URL (`invalid_url`) or oversized request. - `status` - `error` ### 422 — Validation error (e.g. unsupported `format`, slow-path-only field, or `format=html` against a non-HTML content type). - `status` - `error` ### 502 — Fetch service unavailable, content transform failed, or a non-2xx upstream response relayed as the unified Error envelope. On an upstream relay the HTTP status equals the upstream status (code `upstream_error`), so any 4xx/5xx the target returns surfaces here as an Error envelope rather than a raw body (#1462). - `status` - `error` --- URL: https://bytekit.com/docs/api/fetch # Fetch Fetch API — endpoints, requests, and responses. The **Fetch** API. 2 endpoints: - [Fetch a URL (GET)](https://bytekit.com/docs/api/fetch/getfetch) — GET /v1/fetch - [Fetch a URL (POST)](https://bytekit.com/docs/api/fetch/postfetch) — POST /v1/fetch --- URL: https://bytekit.com/docs/api/fetch/postfetch # Fetch a URL (POST) Same as GET /v1/fetch but with a JSON request body. **POST** `/v1/fetch` ## Request Body - `url` (required) - `format` - `country` - `timeout_ms` - `cache_ttl` - `custom` - `markdown_mode` - `markdown_query` - `markdown_links` - `markdown_images` - `with_links_summary` - `with_images_summary` - `markdown_compact` - `markdown_filter_images` - `markdown_include_media` - `markdown_include_warnings` - `markdown_include_stats` ## Responses ### 200 — Content from upstream. ### 400 — Invalid URL (`invalid_url`) or oversized request body. - `status` - `error` ### 422 — Validation error (e.g. malformed JSON, unsupported `format`, slow-path-only field, or `format=html` against a non-HTML content type). - `status` - `error` ### 502 — Fetch service unavailable, content transform failed, or a non-2xx upstream response relayed as the unified Error envelope. On an upstream relay the HTTP status equals the upstream status (code `upstream_error`), so any 4xx/5xx the target returns surfaces here as an Error envelope rather than a raw body (#1462). - `status` - `error` --- URL: https://bytekit.com/docs/api/health/healthcheck # Health check **GET** `/health` ## Responses ### 200 — Service is healthy. - `status` --- URL: https://bytekit.com/docs/api/health # Health Health API — endpoints, requests, and responses. The **Health** API. 1 endpoint: - [Health check](https://bytekit.com/docs/api/health/healthcheck) — GET /health --- URL: https://bytekit.com/docs/api/monitors/createmonitor # Create a change-detection monitor Creates a recurring change-detection monitor for a URL, delivering results via webhook. **POST** `/v1/monitors` ## Request Body - `url` (required) - `type` - `interval_type` (required) - `cron_expression` - `change_threshold` - `notify_on` - `webhook_url` (required) - `webhook_secret` - `webhook_headers` - `options` - `scrape_options` - `metadata` ## Responses ### 201 — Monitor created. - `id` - `type` - `url` - `status` - `interval_type` - `cron_expression` - `next_capture_at` - `notify_on` - `webhook_url` - `webhook_headers` - `change_threshold` - `scrape_options` - `last_content_hash` - `captures_count` - `changes_count` - `skipped_captures` - `last_captured_at` - `last_changed_at` - `consecutive_failures` - `last_error_code` - `suspension_reason` - `metadata` - `created_at` - `updated_at` - `cancelled_at` - `webhook_secret` ### 400 — Bad request. When the `url` (or `webhook_url`) field fails strict validation the error code is `invalid_url` and the `details` object contains: - `field` — the request field that failed (e.g. `"url"`) - `url` — the rejected value - `reason` — one of: `scheme_missing`, `scheme_unsupported`, `scheme_separator_typo`, `host_empty`, `host_invalid`, `host_blocked_private`, `host_blocked_loopback`, `host_too_long`, `host_unicode_not_punycode`, `userinfo_present`, `url_too_long`, `control_chars`, `not_a_url` - `status` - `error` ### 409 — Duplicate monitor for this URL. - `status` - `error` ### 422 — Validation error. - `status` - `error` --- URL: https://bytekit.com/docs/api/monitors/deletemonitor # Cancel monitor **DELETE** `/v1/monitors/{id}` ## Parameters - `id` (path, string, required) ## Responses ### 204 — Monitor cancelled. ### 404 — Resource not found. - `status` - `error` --- URL: https://bytekit.com/docs/api/monitors/getmonitor # Get monitor **GET** `/v1/monitors/{id}` ## Parameters - `id` (path, string, required) ## Responses ### 200 — Successful response. - `id` - `type` - `url` - `status` - `interval_type` - `cron_expression` - `next_capture_at` - `notify_on` - `webhook_url` - `webhook_headers` - `change_threshold` - `scrape_options` - `last_content_hash` - `captures_count` - `changes_count` - `skipped_captures` - `last_captured_at` - `last_changed_at` - `consecutive_failures` - `last_error_code` - `suspension_reason` - `metadata` - `created_at` - `updated_at` - `cancelled_at` ### 404 — Resource not found. - `status` - `error` --- URL: https://bytekit.com/docs/api/monitors # Monitors Monitors API — endpoints, requests, and responses. The **Monitors** API. 6 endpoints: - [List monitors](https://bytekit.com/docs/api/monitors/listmonitors) — GET /v1/monitors - [Create a change-detection monitor](https://bytekit.com/docs/api/monitors/createmonitor) — POST /v1/monitors - [Get monitor](https://bytekit.com/docs/api/monitors/getmonitor) — GET /v1/monitors/:id - [Update monitor](https://bytekit.com/docs/api/monitors/updatemonitor) — PATCH /v1/monitors/:id - [Cancel monitor](https://bytekit.com/docs/api/monitors/deletemonitor) — DELETE /v1/monitors/:id - [List monitor captures](https://bytekit.com/docs/api/monitors/listmonitorcaptures) — GET /v1/monitors/:id/captures --- URL: https://bytekit.com/docs/api/monitors/listmonitorcaptures # List monitor captures **GET** `/v1/monitors/{id}/captures` ## Parameters - `id` (path, string, required) - `limit` (query, integer) - `cursor` (query, string) — Pagination cursor from a previous response. ## Responses ### 200 — Successful response. - `data` - `next_cursor` - `has_more` --- URL: https://bytekit.com/docs/api/monitors/listmonitors # List monitors **GET** `/v1/monitors` ## Parameters - `limit` (query, integer) - `cursor` (query, string) — Pagination cursor from a previous response. - `status` (query, string) ## Responses ### 200 — Successful response. - `data` - `next_cursor` - `has_more` --- URL: https://bytekit.com/docs/api/monitors/updatemonitor # Update monitor **PATCH** `/v1/monitors/{id}` ## Parameters - `id` (path, string, required) ## Request Body - `url` - `interval_type` - `cron_expression` - `change_threshold` - `notify_on` - `webhook_url` - `webhook_secret` - `webhook_headers` - `status` - `options` - `scrape_options` - `metadata` ## Responses ### 200 — Successful response. - `id` - `type` - `url` - `status` - `interval_type` - `cron_expression` - `next_capture_at` - `notify_on` - `webhook_url` - `webhook_headers` - `change_threshold` - `scrape_options` - `last_content_hash` - `captures_count` - `changes_count` - `skipped_captures` - `last_captured_at` - `last_changed_at` - `consecutive_failures` - `last_error_code` - `suspension_reason` - `metadata` - `created_at` - `updated_at` - `cancelled_at` ### 400 — Bad request. When the `url` (or `webhook_url`) field fails strict validation the error code is `invalid_url` and the `details` object contains: - `field` — the request field that failed (e.g. `"url"`) - `url` — the rejected value - `reason` — one of: `scheme_missing`, `scheme_unsupported`, `scheme_separator_typo`, `host_empty`, `host_invalid`, `host_blocked_private`, `host_blocked_loopback`, `host_too_long`, `host_unicode_not_punycode`, `userinfo_present`, `url_too_long`, `control_chars`, `not_a_url` - `status` - `error` ### 404 — Resource not found. - `status` - `error` ### 409 — Duplicate URL. - `status` - `error` ### 422 — Validation error. - `status` - `error` --- URL: https://bytekit.com/docs/api/fetch-bulk/createfetchbulk # Bulk fetch URLs Enqueues multiple URLs for HTTP fetching. Results delivered via webhook. Does not support options that require page rendering (cookies, wait_for_selector, etc.). **POST** `/v1/fetch/bulk` ## Request Body - `urls` (required) - `defaults` - `webhook_url` (required) - `webhook_secret` - `metadata` ## Responses ### 202 — Fetch bulk job accepted. ### 400 — Bad request. When a `url` (or item URL) fails strict validation the error code is `invalid_url` and the `details` object contains: - `field` — the request field that failed (e.g. `"url"`) - `url` — the rejected value - `reason` — one of: `scheme_missing`, `scheme_unsupported`, `scheme_separator_typo`, `host_empty`, `host_invalid`, `host_blocked_private`, `host_blocked_loopback`, `host_too_long`, `host_unicode_not_punycode`, `userinfo_present`, `url_too_long`, `control_chars`, `not_a_url` - `status` - `error` ### 422 — Validation error. `bulk_too_large` is returned when the item count exceeds the account plan's `max_bulk_size` cap (also surfaced as `max_bulk_size` on `GET /v1/account`). - `status` - `error` --- URL: https://bytekit.com/docs/api/fetch-bulk/getfetchbulk # Get fetch bulk job status **GET** `/v1/fetch/bulk/{id}` ## Parameters - `id` (path, string, required) ## Responses ### 200 — Successful response. ### 404 — Resource not found. - `status` - `error` --- URL: https://bytekit.com/docs/api/fetch-bulk # Fetch Bulk Fetch Bulk API — endpoints, requests, and responses. The **Fetch Bulk** API. 2 endpoints: - [Bulk fetch URLs](https://bytekit.com/docs/api/fetch-bulk/createfetchbulk) — POST /v1/fetch/bulk - [Get fetch bulk job status](https://bytekit.com/docs/api/fetch-bulk/getfetchbulk) — GET /v1/fetch/bulk/:id --- URL: https://bytekit.com/docs/api/schema/createschemaextraction # Schema-driven structured extraction Fetches `url` and extracts structured JSON conforming to the supplied JSON Schema (draft 2020-12) using an LLM. Fast-path only — slow-path rendering options (`wait_until=networkidle`, `cookies`, `delay_ms`, custom `headers`, `wait_for_selector`) cause a `400 unsupported_url`. Dual-axis billing: a fixed per-request credit charge **plus** the underlying page's bandwidth, billed exactly like `/v1/scrape`. The bandwidth is still billed when the scrape succeeds but extraction later fails; both axes are refunded only when the scrape itself never runs. Availability is gated by a server-side feature flag; when disabled the endpoint returns `404`. **POST** `/v1/schema` ## Request Body - `url` (required) - `schema` (required) - `prompt` - `model` - `wait_for_selector` - `wait_until` - `cookies` - `delay_ms` - `headers` - `country` - `language` ## Responses ### 200 — Extraction completed (success or graceful extract failure). - `data` - `model` - `validated` - `metrics` - `scrape` - `warnings` ### 400 — Invalid URL (`invalid_url`), unsupported model (`unsupported_model`), or slow-path URL (`unsupported_url`). - `status` - `error` ### 401 — Missing or invalid bearer key. - `status` - `error` ### 402 — Bandwidth quota exhausted (`quota_exceeded`). - `status` - `error` ### 404 — Endpoint not available (feature flag disabled). - `status` - `error` ### 422 — Validation error (`validation_error`) or non-normalizable schema (`schema_incompatible`). - `status` - `error` ### 429 — Per-account rate limit (`rate_limited`) or upstream provider rate limit (`provider_rate_limited`); a `Retry-After` header may be present. - `status` - `error` ### 502 — Underlying scrape failed (`scrape_failed`) or upstream provider error (`provider_error`). - `status` - `error` ### 503 — Extraction provider unconfigured (`provider_unavailable`). - `status` - `error` ### 504 — Upstream provider timed out (`provider_timeout`). - `status` - `error` --- URL: https://bytekit.com/docs/api/schema # Schema Schema API — endpoints, requests, and responses. The **Schema** API. 1 endpoint: - [Schema-driven structured extraction](https://bytekit.com/docs/api/schema/createschemaextraction) — POST /v1/schema --- URL: https://bytekit.com/docs/api/plans # Plans Plans API — endpoints, requests, and responses. The **Plans** API. 1 endpoint: - [List public plan catalog](https://bytekit.com/docs/api/plans/listplans) — GET /v1/plans --- URL: https://bytekit.com/docs/api/plans/listplans # List public plan catalog Returns the catalog of public plans (`is_custom = false`) as the pricing source-of-truth for the dashboard and marketing site. Custom contract plans are excluded. Unauthenticated — this is catalog data, not per-account state (per-account plan lives on `GET /v1/account`). **GET** `/v1/plans` ## Responses ### 200 — The public plan catalog. - `plans` --- URL: https://bytekit.com/docs/api/scrape/createscrape # Scrape a URL Fetches and processes a URL, returning content in one or more formats wrapped in a ScrapeEnvelope. Most requests return synchronously in under a second; requests needing a full page render return HTTP 202 and complete asynchronously. **POST** `/v1/scrape` ## Request Body - `url` (required) - `formats` - `country` - `cookies` - `headers` - `delay_ms` - `timeout_ms` - `async` - `webhook_url` - `events` - `markdown_mode` - `markdown_query` - `markdown_links` - `markdown_images` - `with_links_summary` - `with_images_summary` - `markdown_compact` - `markdown_filter_images` - `markdown_include_media` - `markdown_include_warnings` - `markdown_include_stats` - `token_budget` - `token_encoding` - `mobile` - `remove_base64_images` - `include_tags` - `exclude_tags` - `cache_ttl` - `custom` - `clean_markdown` - `x_internal_scrape_path` ## Responses ### 200 — Scrape completed synchronously. - `schema_version` - `status` - `id` - `url` - `final_url` - `content_type` - `content_length` - `status_code` - `retrieved_at` - `formats` - `metadata` - `tables` - `warnings` - `stats` - `markdown_tokens` - `billing_multiplier` - `billed_bytes` - `custom` - `cache` - `cache_age_s` ### 202 — Scrape accepted for async processing. - `schema_version` - `status` - `id` - `url` - `status_url` - `queued_at` - `events` ### 400 — Bad request. When the `url` field fails strict validation the error code is `invalid_url` and the `details` object contains: - `field` — the request field that failed (e.g. `"url"`) - `url` — the rejected value - `reason` — one of: `scheme_missing`, `scheme_unsupported`, `scheme_separator_typo`, `host_empty`, `host_invalid`, `host_blocked_private`, `host_blocked_loopback`, `host_too_long`, `host_unicode_not_punycode`, `userinfo_present`, `url_too_long`, `control_chars`, `not_a_url` - `status` - `error` ### 422 — Validation error. - `status` - `error` ### 429 — Rate limit exceeded. - `status` - `error` ### 500 — Internal server error. - `status` - `error` --- URL: https://bytekit.com/docs/api/scrape/getscrape # Get scrape result Always returns 200. Check the `status` discriminator for the scrape state. **GET** `/v1/scrape/{id}` ## Parameters - `id` (path, string, required) ## Responses ### 200 — Scrape state (queued, success, or failed). - `schema_version` - `status` - `id` - `url` - `final_url` - `content_type` - `content_length` - `status_code` - `retrieved_at` - `formats` - `metadata` - `tables` - `warnings` - `stats` - `markdown_tokens` - `billing_multiplier` - `billed_bytes` - `custom` - `cache` - `cache_age_s` - `error` - `status_url` - `queued_at` - `events` ### 404 — Resource not found. - `status` - `error` --- URL: https://bytekit.com/docs/api/scrape # Scrape Scrape API — endpoints, requests, and responses. The **Scrape** API. 2 endpoints: - [Scrape a URL](https://bytekit.com/docs/api/scrape/createscrape) — POST /v1/scrape - [Get scrape result](https://bytekit.com/docs/api/scrape/getscrape) — GET /v1/scrape/:id --- URL: https://bytekit.com/docs/api/scrape-bulk/createscrapebulk # Bulk scrape URLs Enqueues multiple URLs for scraping. Results delivered via webhook. Webhook deliveries include `X-ByteKit-Event: scrape.completed` or `X-ByteKit-Event: scrape.failed` so receivers can dispatch on the header without parsing the body shape. **POST** `/v1/scrape/bulk` ## Request Body - `urls` - `items` - `defaults` - `webhook_url` (required) - `webhook_secret` - `metadata` - `custom` ## Responses ### 202 — Bulk scrape job accepted. - `id` - `status` - `total` - `completed` - `failed` - `created_at` - `completed_at` - `total_credits_charged` - `estimated_credits` - `webhook_url` ### 400 — Bad request. When a `url` (or item URL) fails strict validation the error code is `invalid_url` and the `details` object contains: - `field` — the request field that failed (e.g. `"url"`) - `url` — the rejected value - `reason` — one of: `scheme_missing`, `scheme_unsupported`, `scheme_separator_typo`, `host_empty`, `host_invalid`, `host_blocked_private`, `host_blocked_loopback`, `host_too_long`, `host_unicode_not_punycode`, `userinfo_present`, `url_too_long`, `control_chars`, `not_a_url` - `status` - `error` ### 422 — Validation error. `bulk_too_large` is returned when the item count exceeds the account plan's `max_bulk_size` cap (also surfaced as `max_bulk_size` on `GET /v1/account`). - `status` - `error` --- URL: https://bytekit.com/docs/api/scrape-bulk/getscrapebulk # Get scrape bulk job status **GET** `/v1/scrape/bulk/{id}` ## Parameters - `id` (path, string, required) ## Responses ### 200 — Bulk job status with rehydrated item envelopes. Each completed scrape result entry echoes its `custom` payload (resolved per entry as `entry.custom ?? body.custom`) at the same position it occupies in the `/v1/scrape` success envelope. The key is omitted when no `custom` was supplied for that entry. - `id` - `status` - `total` - `completed` - `failed` - `created_at` - `completed_at` - `total_credits_charged` - `estimated_credits` - `webhook_url` ### 404 — Resource not found. - `status` - `error` --- URL: https://bytekit.com/docs/api/scrape-bulk # Scrape Bulk Scrape Bulk API — endpoints, requests, and responses. The **Scrape Bulk** API. 2 endpoints: - [Bulk scrape URLs](https://bytekit.com/docs/api/scrape-bulk/createscrapebulk) — POST /v1/scrape/bulk - [Get scrape bulk job status](https://bytekit.com/docs/api/scrape-bulk/getscrapebulk) — GET /v1/scrape/bulk/:id --- URL: https://bytekit.com/docs/api/screenshots/createscreenshot # Capture a screenshot Sync by default (holds up to 28s). Pass `?async=true` to return 202 immediately. Poll via GET /v1/screenshots/{id}. **POST** `/v1/screenshots` ## Parameters - `async` (query, boolean) ## Request Body - `url` (required) - `device` - `viewport` - `full_page` - `format` - `quality` - `block_ads` - `block_cookie_banners` - `wait_until` - `wait_for_selector` - `delay_ms` - `scroll` - `clip` - `headers` - `cookies` - `dark_mode` - `device_scale_factor` - `include_html` - `country` - `language` - `metadata` ## Responses ### 200 — Screenshot completed (sync mode). - `id` - `status` - `url` - `image_url` - `page_title` - `image_width` - `image_height` - `file_size_bytes` - `credits_charged` - `error_code` - `error_message` - `created_at` - `completed_at` - `expires_at` - `poll_url` ### 202 — Screenshot accepted (async mode or sync timeout). - `id` - `status` - `url` - `image_url` - `page_title` - `image_width` - `image_height` - `file_size_bytes` - `credits_charged` - `error_code` - `error_message` - `created_at` - `completed_at` - `expires_at` - `poll_url` ### 400 — Bad request. When the `url` field fails strict validation the error code is `invalid_url` and the `details` object contains: - `field` — the request field that failed (e.g. `"url"`) - `url` — the rejected value - `reason` — one of: `scheme_missing`, `scheme_unsupported`, `scheme_separator_typo`, `host_empty`, `host_invalid`, `host_blocked_private`, `host_blocked_loopback`, `host_too_long`, `host_unicode_not_punycode`, `userinfo_present`, `url_too_long`, `control_chars`, `not_a_url` - `status` - `error` ### 402 — Quota exceeded. - `status` - `error` ### 422 — Validation error or screenshot failed. - `status` - `error` --- URL: https://bytekit.com/docs/api/screenshots/getscreenshot # Get screenshot status **GET** `/v1/screenshots/{id}` ## Parameters - `id` (path, string, required) ## Responses ### 200 — Screenshot completed or failed. A `completed` screenshot returns the full `ScreenshotResponse` resource (signed `image_url`, dimensions, etc.). A terminal-`failed` screenshot is a successfully retrieved terminal state (like a failed async scrape) and instead returns the unified failed envelope (`Error` schema) populated from the screenshot's own `error_code`/`error_message` (issue #2027). - `id` - `status` - `url` - `image_url` - `page_title` - `image_width` - `image_height` - `file_size_bytes` - `credits_charged` - `error_code` - `error_message` - `created_at` - `completed_at` - `expires_at` - `poll_url` - `error` ### 202 — Screenshot still processing. - `id` - `status` - `url` - `image_url` - `page_title` - `image_width` - `image_height` - `file_size_bytes` - `credits_charged` - `error_code` - `error_message` - `created_at` - `completed_at` - `expires_at` - `poll_url` ### 404 — Resource not found. - `status` - `error` --- URL: https://bytekit.com/docs/api/screenshots # Screenshots Screenshots API — endpoints, requests, and responses. The **Screenshots** API. 2 endpoints: - [Capture a screenshot](https://bytekit.com/docs/api/screenshots/createscreenshot) — POST /v1/screenshots - [Get screenshot status](https://bytekit.com/docs/api/screenshots/getscreenshot) — GET /v1/screenshots/:id --- URL: https://bytekit.com/docs/api/search/createsearch # Search the web Runs a web search for `query` and returns ranked organic results with contiguous `position` values from 1. Use `limit` (1–100, default 10) to request more results in a single call. Results are fetched in pages of 10, so a call consumes `ceil(limit / 10)` credits (capped at 10) from the account's monthly credits bucket — for example `limit=10` costs 1 credit and `limit=35` costs 4. Credits are charged for every page fetched regardless of how many results a page returns. The credit count is reported as `credits_used` in the response. **POST** `/v1/search` ## Request Body - `query` (required) - `type` - `limit` - `country` - `language` - `date_range` ## Responses ### 200 — Search completed. - `id` - `query` - `results` - `related_searches` - `credits_used` ### 400 — Bad request (e.g. missing or empty `query`). - `status` - `error` ### 402 — Payment required — the account is out of credits. - `status` - `error` ### 429 — Rate limit exceeded. - `status` - `error` ### 500 — Internal error (code `internal_error`) — the search request could not be recorded before the provider call. The pre-incremented credits are refunded and no provider call is made. - `status` - `error` ### 502 — Upstream search provider error. The body is a fixed, vendor-masked shape — deliberately NOT the unified `Error` envelope. The upstream provider's identity and message are never surfaced, so the response carries only the constant `error` code `search_provider_error`. - `error` --- URL: https://bytekit.com/docs/api/search # Search Search API — endpoints, requests, and responses. The **Search** API. 1 endpoint: - [Search the web](https://bytekit.com/docs/api/search/createsearch) — POST /v1/search --- URL: https://bytekit.com/docs/api/sitemap/createsitemap # Discover URLs on a domain Crawls a domain to build a sitemap. Returns cached results if available within cache_ttl. Results delivered via webhook and downloadable from results_url. Webhook deliveries include `X-ByteKit-Event: sitemap.completed` or `X-ByteKit-Event: sitemap.failed` so receivers can dispatch on the header without parsing the body shape. **POST** `/v1/sitemap` ## Request Body - `url` (required) - `strategy` - `max_depth` - `max_urls` - `process` - `webhook_url` - `webhook_secret` - `cache_ttl` - `compact` - `metadata` ## Responses ### 202 — Sitemap job accepted (or cached result returned). - `id` - `url` - `strategy` - `max_depth` - `max_urls` - `status` - `total_urls` - `sources` - `total_bytes` - `results_url` - `cache` - `cache_age_s` - `warnings` - `error_code` - `error_message` - `compact` - `process` - `metadata` - `created_at` - `completed_at` - `expires_at` ### 400 — Bad request. When the `url` field fails strict validation the error code is `invalid_url` and the `details` object contains: - `field` — the request field that failed (e.g. `"url"`) - `url` — the rejected value - `reason` — one of: `scheme_missing`, `scheme_unsupported`, `scheme_separator_typo`, `host_empty`, `host_invalid`, `host_blocked_private`, `host_blocked_loopback`, `host_too_long`, `host_unicode_not_punycode`, `userinfo_present`, `url_too_long`, `control_chars`, `not_a_url` - `status` - `error` ### 422 — Validation error. - `status` - `error` --- URL: https://bytekit.com/docs/api/sitemap/getsitemap # Get sitemap job status **GET** `/v1/sitemap/{id}` ## Parameters - `id` (path, string, required) ## Responses ### 200 — Successful response. - `id` - `url` - `strategy` - `max_depth` - `max_urls` - `status` - `total_urls` - `sources` - `total_bytes` - `results_url` - `cache` - `cache_age_s` - `warnings` - `error_code` - `error_message` - `compact` - `process` - `metadata` - `created_at` - `completed_at` - `expires_at` ### 404 — Resource not found. - `status` - `error` --- URL: https://bytekit.com/docs/api/sitemap # Sitemap Sitemap API — endpoints, requests, and responses. The **Sitemap** API. 2 endpoints: - [Discover URLs on a domain](https://bytekit.com/docs/api/sitemap/createsitemap) — POST /v1/sitemap - [Get sitemap job status](https://bytekit.com/docs/api/sitemap/getsitemap) — GET /v1/sitemap/:id --- URL: https://bytekit.com/docs/api/usage/getusage # Get current billing period usage **GET** `/v1/usage` ## Responses ### 200 — Period summary. In bytes or dual billing mode, includes byte fields and structured bandwidth/credits objects. - `period_start` - `period_end` - `credits_used` - `credits_single` - `credits_bulk` - `credits_monitor` - `credits_search` - `credits_limit` - `credits_remaining` - `has_overage` - `overage_credits` - `plan_name` - `plan_price_cents` - `plan_type` - `topup_spent_cents` - `topup_balance_bytes` - `period_days_elapsed` - `bytes_used` - `bytes_single` - `bytes_bulk` - `bytes_scrape` - `bytes_monitor` - `bytes_limit` - `bytes_remaining` - `rollover_bytes` - `topup_bytes` - `request_count` - `request_count_previous_period` - `playground_api_key_id` - `bandwidth` - `credits` --- URL: https://bytekit.com/docs/api/usage/getusagebyendpoint # Get per-endpoint bandwidth breakdown Returns bandwidth usage broken down by endpoint (screenshots, recordings, scrape, scrape_md, fetch, fetch_md, crawl, crawl_links, sitemap, monitors) for a date range. Each row carries raw bytes, the billing multiplier, and billable bytes. Endpoints with zero raw bytes for the range are omitted from `rows`; `totals` is always present. Default range is the last 30 days; max 366 days. **GET** `/v1/usage/by-endpoint` ## Parameters - `from` (query, string) — Start date (YYYY-MM-DD). Default 30 days ago. - `to` (query, string) — End date (YYYY-MM-DD). Default today. - `api_key` (query, string) — Filter by API key (prefixed ID, e.g. `ak_xxx`). When provided, scopes the breakdown to that key only. The key must belong to the requesting account; cross-account keys return an empty breakdown. When absent, aggregates across all keys. ## Responses ### 200 — Successful response. - `from` - `to` - `rows` - `totals` --- URL: https://bytekit.com/docs/api/usage/getusagedaily # Get daily usage breakdown Default range is last 30 days. Max range is 366 days. **GET** `/v1/usage/daily` ## Parameters - `from` (query, string) — Start date (YYYY-MM-DD). Default 30 days ago. - `to` (query, string) — End date (YYYY-MM-DD). Default today. - `api_key` (query, string) — Filter by API key (prefixed ID, e.g. `ak_xxx`). When provided, returns usage rows attributed to that key only. The key must belong to the requesting account; cross-account keys return empty data. Revoked keys still return historical data. When absent, aggregates across all keys. ## Responses ### 200 — Successful response. - `from` - `to` - `data` --- URL: https://bytekit.com/docs/api/usage # Usage Usage API — endpoints, requests, and responses. The **Usage** API. 3 endpoints: - [Get current billing period usage](https://bytekit.com/docs/api/usage/getusage) — GET /v1/usage - [Get daily usage breakdown](https://bytekit.com/docs/api/usage/getusagedaily) — GET /v1/usage/daily - [Get per-endpoint bandwidth breakdown](https://bytekit.com/docs/api/usage/getusagebyendpoint) — GET /v1/usage/by-endpoint --- URL: https://bytekit.com/docs/api/webhooks # Webhooks Webhooks API — endpoints, requests, and responses. The **Webhooks** API. 2 endpoints: - [List webhook deliveries](https://bytekit.com/docs/api/webhooks/listwebhookdeliveries) — GET /v1/webhook-deliveries - [Retry a webhook delivery](https://bytekit.com/docs/api/webhooks/retrywebhookdelivery) — POST /v1/webhook-deliveries/:id/retry --- URL: https://bytekit.com/docs/api/webhooks/listwebhookdeliveries # List webhook deliveries **GET** `/v1/webhook-deliveries` ## Parameters - `limit` (query, integer) - `cursor` (query, string) — Pagination cursor from a previous response. - `status` (query, string) - `since` (query, string) — Filter deliveries created after this ISO date. ## Responses ### 200 — Successful response. - `data` - `next_cursor` - `has_more` --- URL: https://bytekit.com/docs/api/webhooks/retrywebhookdelivery # Retry a webhook delivery Requeues a failed or exhausted webhook delivery for another attempt. **POST** `/v1/webhook-deliveries/{id}/retry` ## Parameters - `id` (path, string, required) ## Responses ### 200 — Successful response. - `id` - `event_type` - `target_url` - `status` - `attempts` - `max_attempts` - `next_attempt_at` - `response_status` - `response_ms` - `created_at` - `delivered_at` - `screenshot_id` - `recording_id` - `bulk_id` - `monitor_id` - `scrape_id` ### 404 — Delivery not found or not in a retryable state. - `status` - `error` --- URL: https://bytekit.com/docs/sdk/python/account # Account API functions for the Account resource. # Account Functions for interacting with the `account` API resource. Each operation ships in four forms — `sync` / `sync_detailed` and their `async` counterparts. ## Get Account Get account details Returns account, plan, and current API key details, including the `api_key` and `subscription` fields. **Call styles** ```python sync_detailed(client=client) -> Response[Union['AccountBearerResponse', 'AccountSessionResponse']] sync(client=client) -> Optional[Union['AccountBearerResponse', 'AccountSessionResponse']] await asyncio_detailed(client=client) -> Response[Union['AccountBearerResponse', 'AccountSessionResponse']] await asyncio(client=client) -> Optional[Union['AccountBearerResponse', 'AccountSessionResponse']] ``` --- URL: https://bytekit.com/docs/sdk/python/bulk # Bulk API functions for the Bulk resource. # Bulk Functions for interacting with the `bulk` API resource. Each operation ships in four forms — `sync` / `sync_detailed` and their `async` counterparts. ## Create Bulk Create mixed bulk job Enqueues screenshots or scrapes in a single batch. Provide `urls` (simple list) or `items` (per-item config), not both. Webhook deliveries include `X-ByteKit-Event: bulk.completed` so receivers can dispatch on the header without parsing the body shape. **Parameters** - `body (CreateBulkBody): Provide `urls` (simple list) OR `items` (per-item config), not` - `both. `type` selects the capture kind per entry — only` - ``screenshot` and `scrape` are accepted (`recording` is` - `deactivated). Behaviour fields may be set per item or via` - ``defaults`; a per-item value overrides the matching default.` **Call styles** ```python sync_detailed(client=client, body=body) -> Response[Union[CreateBulkResponse202, Error]] sync(client=client, body=body) -> Optional[Union[CreateBulkResponse202, Error]] await asyncio_detailed(client=client, body=body) -> Response[Union[CreateBulkResponse202, Error]] await asyncio(client=client, body=body) -> Optional[Union[CreateBulkResponse202, Error]] ``` ## Delete Bulk Cancel a bulk job Cancels an in-flight bulk job: drains its still-queued (pending) child jobs and refunds the quota held for exactly those drained children. Children already completed, failed, or in progress are untouched and keep their charge. Idempotent — cancelling an already-terminal job (completed, failed, or cancelled) is a no-op that returns 200 with the job's real, unchanged status and refunds nothing. **Parameters** - `id (str)` **Call styles** ```python sync_detailed(id=id, client=client) -> Response[Union[DeleteBulkResponse200, Error]] sync(id=id, client=client) -> Optional[Union[DeleteBulkResponse200, Error]] await asyncio_detailed(id=id, client=client) -> Response[Union[DeleteBulkResponse200, Error]] await asyncio(id=id, client=client) -> Optional[Union[DeleteBulkResponse200, Error]] ``` ## Get Bulk Get bulk job status **Parameters** - `id (str)` **Call styles** ```python sync_detailed(id=id, client=client) -> Response[Union[Error, GetBulkResponse200]] sync(id=id, client=client) -> Optional[Union[Error, GetBulkResponse200]] await asyncio_detailed(id=id, client=client) -> Response[Union[Error, GetBulkResponse200]] await asyncio(id=id, client=client) -> Optional[Union[Error, GetBulkResponse200]] ``` ## List Bulk Screenshots List bulk job items **Parameters** - `id (str)` **Call styles** ```python sync_detailed(id=id, client=client) -> Response[ListBulkScreenshotsResponse200] sync(id=id, client=client) -> Optional[ListBulkScreenshotsResponse200] await asyncio_detailed(id=id, client=client) -> Response[ListBulkScreenshotsResponse200] await asyncio(id=id, client=client) -> Optional[ListBulkScreenshotsResponse200] ``` --- URL: https://bytekit.com/docs/sdk/python/client # Client HTTP client classes for the ByteKit Python SDK. # Client Reference The ByteKit Python SDK provides two client classes for making API requests. ## Client A class for keeping track of data related to the API The following are accepted as keyword arguments and will be used to construct httpx Clients internally: ``base_url``: The base URL for the API, all requests are made to a relative path to this URL ``cookies``: A dictionary of cookies to be sent with every request ``headers``: A dictionary of headers to be sent with every request ``timeout``: The maximum amount of a time a request can take. API functions will raise httpx.TimeoutException if this is exceeded. ``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production, but can be set to False for testing purposes. ``follow_redirects``: Whether or not to follow redirects. Default value is False. ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor. Attributes: raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a status code that was not documented in the source OpenAPI document. Can also be provided as a keyword argument to the constructor. Explicit constructor arguments always win over these defaults (explicit arg > default). ### with_headers ```python with_headers(headers: dict[str, str]) -> 'Client' ``` Get a new client matching this one with additional headers ### with_cookies ```python with_cookies(cookies: dict[str, str]) -> 'Client' ``` Get a new client matching this one with additional cookies ### with_timeout ```python with_timeout(timeout: httpx.Timeout) -> 'Client' ``` Get a new client matching this one with a new timeout (in seconds) ### set_httpx_client ```python set_httpx_client(client: httpx.Client) -> 'Client' ``` Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. ### get_httpx_client ```python get_httpx_client() -> httpx.Client ``` Get the underlying httpx.Client, constructing a new one if not previously set ### set_async_httpx_client ```python set_async_httpx_client(async_client: httpx.AsyncClient) -> 'Client' ``` Manually the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. ### get_async_httpx_client ```python get_async_httpx_client() -> httpx.AsyncClient ``` Get the underlying httpx.AsyncClient, constructing a new one if not previously set ## AuthenticatedClient A Client which has been authenticated for use on secured endpoints The following are accepted as keyword arguments and will be used to construct httpx Clients internally: ``base_url``: The base URL for the API, all requests are made to a relative path to this URL ``cookies``: A dictionary of cookies to be sent with every request ``headers``: A dictionary of headers to be sent with every request ``timeout``: The maximum amount of a time a request can take. API functions will raise httpx.TimeoutException if this is exceeded. ``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production, but can be set to False for testing purposes. ``follow_redirects``: Whether or not to follow redirects. Default value is False. ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor. Attributes: raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a status code that was not documented in the source OpenAPI document. Can also be provided as a keyword argument to the constructor. Explicit constructor arguments always win over these defaults (explicit arg > default). token: The token to use for authentication prefix: The prefix to use for the Authorization header auth_header_name: The name of the Authorization header ### search ```python search(query: str, type: Optional[str] = None, limit: Optional[int] = None, country: Optional[str] = None, language: Optional[str] = None, date_range: Optional[str] = None) -> 'CreateSearchResponse200' ``` Run a web search and return ranked organic results. Thin, statically-declared delegate to the hand-written ``_search`` appended at the bottom of this module; the full contract (argument semantics, the ``errors.UnexpectedStatus`` guarantee) is documented there. ### with_headers ```python with_headers(headers: dict[str, str]) -> 'AuthenticatedClient' ``` Get a new client matching this one with additional headers ### with_cookies ```python with_cookies(cookies: dict[str, str]) -> 'AuthenticatedClient' ``` Get a new client matching this one with additional cookies ### with_timeout ```python with_timeout(timeout: httpx.Timeout) -> 'AuthenticatedClient' ``` Get a new client matching this one with a new timeout (in seconds) ### set_httpx_client ```python set_httpx_client(client: httpx.Client) -> 'AuthenticatedClient' ``` Manually set the underlying httpx.Client **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. ### get_httpx_client ```python get_httpx_client() -> httpx.Client ``` Get the underlying httpx.Client, constructing a new one if not previously set ### set_async_httpx_client ```python set_async_httpx_client(async_client: httpx.AsyncClient) -> 'AuthenticatedClient' ``` Manually the underlying httpx.AsyncClient **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout. ### get_async_httpx_client ```python get_async_httpx_client() -> httpx.AsyncClient ``` Get the underlying httpx.AsyncClient, constructing a new one if not previously set --- URL: https://bytekit.com/docs/sdk/python/fetch-bulk # Fetch Bulk API functions for the Fetch Bulk resource. # Fetch Bulk Functions for interacting with the `fetch_bulk` API resource. Each operation ships in four forms — `sync` / `sync_detailed` and their `async` counterparts. ## Create Fetch Bulk Bulk fetch URLs Enqueues multiple URLs for HTTP fetching. Results delivered via webhook. Does not support options that require page rendering (cookies, wait_for_selector, etc.). **Parameters** - `body (CreateFetchBulkBody)` **Call styles** ```python sync_detailed(client=client, body=body) -> Response[Union[CreateFetchBulkResponse202, Error]] sync(client=client, body=body) -> Optional[Union[CreateFetchBulkResponse202, Error]] await asyncio_detailed(client=client, body=body) -> Response[Union[CreateFetchBulkResponse202, Error]] await asyncio(client=client, body=body) -> Optional[Union[CreateFetchBulkResponse202, Error]] ``` ## Get Fetch Bulk Get fetch bulk job status **Parameters** - `id (str)` **Call styles** ```python sync_detailed(id=id, client=client) -> Response[Union[Error, GetFetchBulkResponse200]] sync(id=id, client=client) -> Optional[Union[Error, GetFetchBulkResponse200]] await asyncio_detailed(id=id, client=client) -> Response[Union[Error, GetFetchBulkResponse200]] await asyncio(id=id, client=client) -> Optional[Union[Error, GetFetchBulkResponse200]] ``` --- URL: https://bytekit.com/docs/sdk/python/fetch # Fetch API functions for the Fetch resource. # Fetch Functions for interacting with the `fetch` API resource. Each operation ships in four forms — `sync` / `sync_detailed` and their `async` counterparts. ## Get Fetch Fetch a URL (GET) Direct HTTP fetch. The response body IS the content; metadata is returned in X-Fetch-* headers. Omit `format` for raw passthrough. Use `format=markdown` or `format=html` for conversion. **Parameters** - `url_query (str)` - `format_ (Union[Unset, GetFetchFormat])` - `country (Union[Unset, str])` - `timeout_ms (Union[Unset, int])` - `cache_ttl (Union[GetFetchCacheTtlType1, Unset, str])` **Call styles** ```python sync_detailed(client=client, url_query=url_query, format_=format_, country=country, timeout_ms=timeout_ms, cache_ttl=cache_ttl) -> Response[Union[Error, str]] sync(client=client, url_query=url_query, format_=format_, country=country, timeout_ms=timeout_ms, cache_ttl=cache_ttl) -> Optional[Union[Error, str]] await asyncio_detailed(client=client, url_query=url_query, format_=format_, country=country, timeout_ms=timeout_ms, cache_ttl=cache_ttl) -> Response[Union[Error, str]] await asyncio(client=client, url_query=url_query, format_=format_, country=country, timeout_ms=timeout_ms, cache_ttl=cache_ttl) -> Optional[Union[Error, str]] ``` ## Post Fetch Fetch a URL (POST) Same as GET /v1/fetch but with a JSON request body. **Parameters** - `body (FetchRequest)` **Call styles** ```python sync_detailed(client=client, body=body) -> Response[Union[Error, str]] sync(client=client, body=body) -> Optional[Union[Error, str]] await asyncio_detailed(client=client, body=body) -> Response[Union[Error, str]] await asyncio(client=client, body=body) -> Optional[Union[Error, str]] ``` --- URL: https://bytekit.com/docs/sdk/python/monitors # Monitors API functions for the Monitors resource. # Monitors Functions for interacting with the `monitors` API resource. Each operation ships in four forms — `sync` / `sync_detailed` and their `async` counterparts. First-capture behavior is the same for both monitor types: the first capture is a silent baseline (`has_change=False`, `change_pct=None`) with no `change_detected` webhook, whether the monitor is `scrape` or `screenshot`. From the second capture onward both types diff against the previous capture. ## Create Monitor Create a change-detection monitor Creates a recurring change-detection monitor for a URL, delivering results via webhook. **Parameters** - `body (MonitorCreateRequest)` **Call styles** ```python sync_detailed(client=client, body=body) -> Response[Union[Error, MonitorCreateResponse]] sync(client=client, body=body) -> Optional[Union[Error, MonitorCreateResponse]] await asyncio_detailed(client=client, body=body) -> Response[Union[Error, MonitorCreateResponse]] await asyncio(client=client, body=body) -> Optional[Union[Error, MonitorCreateResponse]] ``` ## Delete Monitor Cancel monitor **Parameters** - `id (str)` **Call styles** ```python sync_detailed(id=id, client=client) -> Response[Union[Any, Error]] sync(id=id, client=client) -> Optional[Union[Any, Error]] await asyncio_detailed(id=id, client=client) -> Response[Union[Any, Error]] await asyncio(id=id, client=client) -> Optional[Union[Any, Error]] ``` ## Get Monitor Get monitor **Parameters** - `id (str)` **Call styles** ```python sync_detailed(id=id, client=client) -> Response[Union[Error, MonitorResponse]] sync(id=id, client=client) -> Optional[Union[Error, MonitorResponse]] await asyncio_detailed(id=id, client=client) -> Response[Union[Error, MonitorResponse]] await asyncio(id=id, client=client) -> Optional[Union[Error, MonitorResponse]] ``` ## List Monitor Captures List monitor captures **Parameters** - `id (str)` - `limit (Union[Unset, int])` - `cursor (Union[Unset, str])` **Call styles** ```python sync_detailed(id=id, client=client, limit=limit, cursor=cursor) -> Response[ListMonitorCapturesResponse200] sync(id=id, client=client, limit=limit, cursor=cursor) -> Optional[ListMonitorCapturesResponse200] await asyncio_detailed(id=id, client=client, limit=limit, cursor=cursor) -> Response[ListMonitorCapturesResponse200] await asyncio(id=id, client=client, limit=limit, cursor=cursor) -> Optional[ListMonitorCapturesResponse200] ``` ## List Monitors List monitors **Parameters** - `limit (Union[Unset, int])` - `cursor (Union[Unset, str])` - `status (Union[Unset, ListMonitorsStatus])` **Call styles** ```python sync_detailed(client=client, limit=limit, cursor=cursor, status=status) -> Response[ListMonitorsResponse200] sync(client=client, limit=limit, cursor=cursor, status=status) -> Optional[ListMonitorsResponse200] await asyncio_detailed(client=client, limit=limit, cursor=cursor, status=status) -> Response[ListMonitorsResponse200] await asyncio(client=client, limit=limit, cursor=cursor, status=status) -> Optional[ListMonitorsResponse200] ``` ## Update Monitor Update monitor **Parameters** - `id (str)` - `body (MonitorUpdateRequest)` **Call styles** ```python sync_detailed(id=id, client=client, body=body) -> Response[Union[Error, MonitorResponse]] sync(id=id, client=client, body=body) -> Optional[Union[Error, MonitorResponse]] await asyncio_detailed(id=id, client=client, body=body) -> Response[Union[Error, MonitorResponse]] await asyncio(id=id, client=client, body=body) -> Optional[Union[Error, MonitorResponse]] ``` --- URL: https://bytekit.com/docs/sdk/python/scrape-bulk # Scrape Bulk API functions for the Scrape Bulk resource. # Scrape Bulk Functions for interacting with the `scrape_bulk` API resource. Each operation ships in four forms — `sync` / `sync_detailed` and their `async` counterparts. ## Create Scrape Bulk Bulk scrape URLs Enqueues multiple URLs for scraping. Results delivered via webhook. Webhook deliveries include `X-ByteKit-Event: scrape.completed` or `X-ByteKit-Event: scrape.failed` so receivers can dispatch on the header without parsing the body shape. **Parameters** - `body (CreateScrapeBulkBody)` **Call styles** ```python sync_detailed(client=client, body=body) -> Response[Union[CreateScrapeBulkResponse202, Error]] sync(client=client, body=body) -> Optional[Union[CreateScrapeBulkResponse202, Error]] await asyncio_detailed(client=client, body=body) -> Response[Union[CreateScrapeBulkResponse202, Error]] await asyncio(client=client, body=body) -> Optional[Union[CreateScrapeBulkResponse202, Error]] ``` ## Get Scrape Bulk Get scrape bulk job status **Parameters** - `id (str)` **Call styles** ```python sync_detailed(id=id, client=client) -> Response[Union[Error, GetScrapeBulkResponse200]] sync(id=id, client=client) -> Optional[Union[Error, GetScrapeBulkResponse200]] await asyncio_detailed(id=id, client=client) -> Response[Union[Error, GetScrapeBulkResponse200]] await asyncio(id=id, client=client) -> Optional[Union[Error, GetScrapeBulkResponse200]] ``` --- URL: https://bytekit.com/docs/sdk/python/scrape # Scrape API functions for the Scrape resource. # Scrape Functions for interacting with the `scrape` API resource. Each operation ships in four forms — `sync` / `sync_detailed` and their `async` counterparts. ## Create Scrape Scrape a URL Fetches and processes a URL, returning content in one or more formats wrapped in a ScrapeEnvelope. Most requests return synchronously in under a second; requests needing a full page render return HTTP 202 and complete asynchronously. **Parameters** - `body (ScrapeRequest)` **Call styles** ```python sync_detailed(client=client, body=body) -> Response[Union[Error, ScrapeQueuedEnvelope, ScrapeSuccessEnvelope]] sync(client=client, body=body) -> Optional[Union[Error, ScrapeQueuedEnvelope, ScrapeSuccessEnvelope]] await asyncio_detailed(client=client, body=body) -> Response[Union[Error, ScrapeQueuedEnvelope, ScrapeSuccessEnvelope]] await asyncio(client=client, body=body) -> Optional[Union[Error, ScrapeQueuedEnvelope, ScrapeSuccessEnvelope]] ``` ## Get Scrape Get scrape result Always returns 200. Check the `status` discriminator for the scrape state. **Parameters** - `id (str)` **Call styles** ```python sync_detailed(id=id, client=client) -> Response[Union[Error, Union['ScrapeErrorEnvelope', 'ScrapeQueuedEnvelope', 'ScrapeSuccessEnvelope']]] sync(id=id, client=client) -> Optional[Union[Error, Union['ScrapeErrorEnvelope', 'ScrapeQueuedEnvelope', 'ScrapeSuccessEnvelope']]] await asyncio_detailed(id=id, client=client) -> Response[Union[Error, Union['ScrapeErrorEnvelope', 'ScrapeQueuedEnvelope', 'ScrapeSuccessEnvelope']]] await asyncio(id=id, client=client) -> Optional[Union[Error, Union['ScrapeErrorEnvelope', 'ScrapeQueuedEnvelope', 'ScrapeSuccessEnvelope']]] ``` --- URL: https://bytekit.com/docs/sdk/python/screenshots # Screenshots API functions for the Screenshots resource. # Screenshots Functions for interacting with the `screenshots` API resource. Each operation ships in four forms — `sync` / `sync_detailed` and their `async` counterparts. ## Create Screenshot Capture a screenshot Sync by default (holds up to 28s). Pass `?async=true` to return 202 immediately. Poll via GET /v1/screenshots/\{id\}. **Parameters** - `async_ (Union[Unset, bool])` - `body (ScreenshotRequest)` **Call styles** ```python sync_detailed(client=client, body=body, async_=async_) -> Response[Union[Error, ScreenshotResponse]] sync(client=client, body=body, async_=async_) -> Optional[Union[Error, ScreenshotResponse]] await asyncio_detailed(client=client, body=body, async_=async_) -> Response[Union[Error, ScreenshotResponse]] await asyncio(client=client, body=body, async_=async_) -> Optional[Union[Error, ScreenshotResponse]] ``` ## Get Screenshot Get screenshot status **Parameters** - `id (str)` **Call styles** ```python sync_detailed(id=id, client=client) -> Response[Union[Error, ScreenshotResponse, Union['Error', 'ScreenshotResponse']]] sync(id=id, client=client) -> Optional[Union[Error, ScreenshotResponse, Union['Error', 'ScreenshotResponse']]] await asyncio_detailed(id=id, client=client) -> Response[Union[Error, ScreenshotResponse, Union['Error', 'ScreenshotResponse']]] await asyncio(id=id, client=client) -> Optional[Union[Error, ScreenshotResponse, Union['Error', 'ScreenshotResponse']]] ``` --- URL: https://bytekit.com/docs/sdk/python/search # Search API functions for the Search resource. # Search Functions for interacting with the `search` API resource. Each operation ships in four forms — `sync` / `sync_detailed` and their `async` counterparts. ## Create Search Search the web Runs a web search for `query` and returns ranked organic results with contiguous `position` values from 1. Use `limit` (1–100, default 10) to request more results in a single call. Results are fetched in pages of 10, so a call consumes `ceil(limit / 10)` credits (capped at 10) from the account's monthly credits bucket — for example `limit=10` costs 1 credit and `limit=35` costs 4. Credits are charged for every page fetched regardless of how many results a page returns. The credit count is reported as `credits_used` in the response. **Parameters** - `body (CreateSearchBody)` **Call styles** ```python sync_detailed(client=client, body=body) -> Response[Union[CreateSearchResponse200, CreateSearchResponse502, Error]] sync(client=client, body=body) -> Optional[Union[CreateSearchResponse200, CreateSearchResponse502, Error]] await asyncio_detailed(client=client, body=body) -> Response[Union[CreateSearchResponse200, CreateSearchResponse502, Error]] await asyncio(client=client, body=body) -> Optional[Union[CreateSearchResponse200, CreateSearchResponse502, Error]] ``` --- URL: https://bytekit.com/docs/sdk/python/sitemap # Sitemap API functions for the Sitemap resource. # Sitemap Functions for interacting with the `sitemap` API resource. Each operation ships in four forms — `sync` / `sync_detailed` and their `async` counterparts. ## Create Sitemap Discover URLs on a domain Crawls a domain to build a sitemap. Returns cached results if available within cache_ttl. Results delivered via webhook and downloadable from results_url. Webhook deliveries include `X-ByteKit-Event: sitemap.completed` or `X-ByteKit-Event: sitemap.failed` so receivers can dispatch on the header without parsing the body shape. **Parameters** - `body (SitemapRequest)` **Call styles** ```python sync_detailed(client=client, body=body) -> Response[Union[Error, SitemapResponse]] sync(client=client, body=body) -> Optional[Union[Error, SitemapResponse]] await asyncio_detailed(client=client, body=body) -> Response[Union[Error, SitemapResponse]] await asyncio(client=client, body=body) -> Optional[Union[Error, SitemapResponse]] ``` ## Get Sitemap Get sitemap job status **Parameters** - `id (str)` **Call styles** ```python sync_detailed(id=id, client=client) -> Response[Union[Error, SitemapResponse]] sync(id=id, client=client) -> Optional[Union[Error, SitemapResponse]] await asyncio_detailed(id=id, client=client) -> Response[Union[Error, SitemapResponse]] await asyncio(id=id, client=client) -> Optional[Union[Error, SitemapResponse]] ``` --- URL: https://bytekit.com/docs/sdk/python/usage # Usage API functions for the Usage resource. # Usage Functions for interacting with the `usage` API resource. Each operation ships in four forms — `sync` / `sync_detailed` and their `async` counterparts. ## Get Usage Get current billing period usage **Call styles** ```python sync_detailed(client=client) -> Response[GetUsageResponse200] sync(client=client) -> Optional[GetUsageResponse200] await asyncio_detailed(client=client) -> Response[GetUsageResponse200] await asyncio(client=client) -> Optional[GetUsageResponse200] ``` ## Get Usage By Endpoint Get per-endpoint bandwidth breakdown Returns bandwidth usage broken down by endpoint (screenshots, recordings, scrape, scrape_md, fetch, fetch_md, crawl, crawl_links, sitemap, monitors) for a date range. Each row carries raw bytes, the billing multiplier, and billable bytes. Endpoints with zero raw bytes for the range are omitted from `rows`; `totals` is always present. Default range is the last 30 days; max 366 days. **Parameters** - `from_ (Union[Unset, datetime.date])` - `to (Union[Unset, datetime.date])` - `api_key (Union[Unset, str])` **Call styles** ```python sync_detailed(client=client, from_=from_, to=to, api_key=api_key) -> Response[GetUsageByEndpointResponse200] sync(client=client, from_=from_, to=to, api_key=api_key) -> Optional[GetUsageByEndpointResponse200] await asyncio_detailed(client=client, from_=from_, to=to, api_key=api_key) -> Response[GetUsageByEndpointResponse200] await asyncio(client=client, from_=from_, to=to, api_key=api_key) -> Optional[GetUsageByEndpointResponse200] ``` ## Get Usage Daily Get daily usage breakdown Default range is last 30 days. Max range is 366 days. **Parameters** - `from_ (Union[Unset, datetime.date])` - `to (Union[Unset, datetime.date])` - `api_key (Union[Unset, str])` **Call styles** ```python sync_detailed(client=client, from_=from_, to=to, api_key=api_key) -> Response[GetUsageDailyResponse200] sync(client=client, from_=from_, to=to, api_key=api_key) -> Optional[GetUsageDailyResponse200] await asyncio_detailed(client=client, from_=from_, to=to, api_key=api_key) -> Response[GetUsageDailyResponse200] await asyncio(client=client, from_=from_, to=to, api_key=api_key) -> Optional[GetUsageDailyResponse200] ``` --- URL: https://bytekit.com/docs/sdk/python/webhooks # Webhooks API functions for the Webhooks resource. # Webhooks Functions for interacting with the `webhooks` API resource. Each operation ships in four forms — `sync` / `sync_detailed` and their `async` counterparts. ## List Webhook Deliveries List webhook deliveries **Parameters** - `limit (Union[Unset, int])` - `cursor (Union[Unset, str])` - `status (Union[Unset, ListWebhookDeliveriesStatus])` - `since (Union[Unset, datetime.datetime])` **Call styles** ```python sync_detailed(client=client, limit=limit, cursor=cursor, status=status, since=since) -> Response[ListWebhookDeliveriesResponse200] sync(client=client, limit=limit, cursor=cursor, status=status, since=since) -> Optional[ListWebhookDeliveriesResponse200] await asyncio_detailed(client=client, limit=limit, cursor=cursor, status=status, since=since) -> Response[ListWebhookDeliveriesResponse200] await asyncio(client=client, limit=limit, cursor=cursor, status=status, since=since) -> Optional[ListWebhookDeliveriesResponse200] ``` ## Retry Webhook Delivery Retry a webhook delivery Requeues a failed or exhausted webhook delivery for another attempt. **Parameters** - `id (str)` **Call styles** ```python sync_detailed(id=id, client=client) -> Response[Union[Error, WebhookDeliveryResponse]] sync(id=id, client=client) -> Optional[Union[Error, WebhookDeliveryResponse]] await asyncio_detailed(id=id, client=client) -> Response[Union[Error, WebhookDeliveryResponse]] await asyncio(id=id, client=client) -> Optional[Union[Error, WebhookDeliveryResponse]] ``` --- URL: https://bytekit.com/docs/sdk/typescript/account # Account Account info and API key management. # Account Account info and API key management. Accessed via `client.account`. ## get ```ts client.account.get(requestOptions?: RequestOptions): Promise<GetAccountResponse> ``` GET /v1/account — get the authenticated account. `requestOptions` carries the CLIENT-side abort budget (`requestTimeoutMs`) and an optional caller `signal`; both override the constructor's `timeoutMs` default. ## apiKeys ### create ```ts client.account.apiKeys.create(opts?: CreateApiKeyRequest, requestOptions?: RequestOptions): Promise<CreateApiKeyResponse> ``` POST /v1/account/api-keys — create a new API key. `opts` is optional: the spec declares the request body itself as optional, so `create()` with no arguments is valid and sends no body. ### list ```ts client.account.apiKeys.list(requestOptions?: RequestOptions): Promise<ListApiKeysResponse> ``` GET /v1/account/api-keys — list all API keys for the account ### get ```ts client.account.apiKeys.get(id: ApiKeyId, requestOptions?: RequestOptions): Promise<GetApiKeyResponse> ``` GET /v1/account/api-keys/\{id\} — get an API key by ID ### update ```ts client.account.apiKeys.update(id: ApiKeyId, opts: UpdateApiKeyRequest, requestOptions?: RequestOptions): Promise<UpdateApiKeyResponse> ``` PATCH /v1/account/api-keys/\{id\} — update an API key ### revoke ```ts client.account.apiKeys.revoke(id: ApiKeyId, requestOptions?: RequestOptions): Promise<void> ``` DELETE /v1/account/api-keys/\{id\} — revoke an API key (idempotent). Returns 204 No Content: `operations['revokeApiKey']['responses'][204]` declares `content?: never`, so there is no JSON arm to derive a payload from — hence `Promise<void>`. `client.request()` short-circuits an empty body (204 or any empty response) to `undefined` before it would parse the body as JSON (#2450), so this method resolves cleanly on the gateway's empty 204 body. ### reveal ```ts client.account.apiKeys.reveal(id: ApiKeyId, requestOptions?: RequestOptions): Promise<RevealApiKeyResponse> ``` POST /v1/account/api-keys/\{id\}/reveal — reveal the plaintext API key value --- URL: https://bytekit.com/docs/sdk/typescript/bulk # Bulk Fan out many URLs in parallel with per-item webhooks. # Bulk Fan out many URLs in parallel with per-item webhooks. Accessed via `client.bulk`. ## create ```ts client.bulk.create(opts: BulkCreateRequest, requestOptions?: RequestOptions): Promise<BulkCreateResponse> ``` POST /v1/bulk — create a bulk screenshots job. `requestOptions` carries the CLIENT-side abort budget (`requestTimeoutMs`) and an optional caller `signal`; both override the constructor's `timeoutMs` default. ## get ```ts client.bulk.get(id: BulkId, requestOptions?: RequestOptions): Promise<BulkGetResponse> ``` GET /v1/bulk/\{id\} — get a bulk job status ## cancel ```ts client.bulk.cancel(id: BulkId, requestOptions?: RequestOptions): Promise<BulkCancelResponse> ``` DELETE /v1/bulk/\{id\} — cancel an in-flight bulk job ## screenshots ### list ```ts client.bulk.screenshots.list(id: BulkId, requestOptions?: RequestOptions): Promise<ListBulkScreenshotsResponse> ``` GET /v1/bulk/\{id\}/screenshots — list screenshots in a bulk job --- URL: https://bytekit.com/docs/sdk/typescript/client # Client Instantiate and configure the ByteKit TypeScript client. # Client The ByteKit TypeScript SDK is a thin, type-safe wrapper over the REST API. Create a client with your API key, then call resource methods. ```ts import { ByteKit } from '@hunt-labs/bytekit-sdk'; const client = new ByteKit({ apiKey: process.env.BYTEKIT_API_KEY!, // your sk_live_… key }); ``` ## Options (`ByteKitOptions`) - `apiKey` (required) — `string` - `baseUrl` — `string` Defaults to `https://api.bytekit.com`. - `timeoutMs` — `number` — Default CLIENT-SIDE request timeout in ms — the abort budget for the whole HTTP round-trip made by this SDK, covering time-to-headers **and** the response body read (so a large legitimate body counts against it too). `0` disables the SDK-side timeout. Default 120000. NOT the same thing as `ScrapeOpts.timeout_ms`, which is the SERVER-side budget the gateway applies to the scrape itself. Override per call with `RequestOptions.requestTimeoutMs`. ## Resources The client exposes one property per resource: - [`client.scrape`](https://bytekit.com/docs/sdk/typescript/scrape) — Fetch a URL as raw HTML, clean markdown, or structured content. - [`client.screenshots`](https://bytekit.com/docs/sdk/typescript/screenshots) — Capture full-page or viewport screenshots. - [`client.bulk`](https://bytekit.com/docs/sdk/typescript/bulk) — Fan out many URLs in parallel with per-item webhooks. - [`client.fetch`](https://bytekit.com/docs/sdk/typescript/fetch) — Low-latency raw HTTP fetch with optional conversion. - [`client.monitors`](https://bytekit.com/docs/sdk/typescript/monitors) — Watch a URL on a schedule and webhook on change. - [`client.sitemap`](https://bytekit.com/docs/sdk/typescript/sitemap) — Discover URLs from a domain's sitemap or by crawling. - [`client.search`](https://bytekit.com/docs/sdk/typescript/search) — Run a web search and return ranked organic results. - [`client.account`](https://bytekit.com/docs/sdk/typescript/account) — Account info and API key management. - [`client.usage`](https://bytekit.com/docs/sdk/typescript/usage) — Billing-period and daily usage breakdowns. - [`client.webhooks`](https://bytekit.com/docs/sdk/typescript/webhooks) — List and retry webhook deliveries. ## Errors Any non-2xx response throws a `ByteKitError` carrying the HTTP `status` and the API `code`. ```ts import { ByteKit, ByteKitError } from '@hunt-labs/bytekit-sdk'; try { await client.scrape.create({ url: 'https://example.com' }); } catch (err) { if (err instanceof ByteKitError) { console.error(`${err.status} ${err.code}: ${err.message}`); } } ``` --- URL: https://bytekit.com/docs/sdk/typescript/fetch-bulk # Fetch Bulk Create and poll bulk fetch jobs. # Fetch Bulk Create and poll bulk fetch jobs. Accessed via `client.fetch.bulk`. ## create ```ts client.fetch.bulk.create(opts: FetchBulkCreateRequest, requestOptions?: RequestOptions): Promise<FetchBulkCreateResponse> ``` POST /v1/fetch/bulk — create a bulk fetch job ## get ```ts client.fetch.bulk.get(id: BulkId, requestOptions?: RequestOptions): Promise<FetchBulkGetResponse> ``` GET /v1/fetch/bulk/\{id\} — get a bulk fetch job --- URL: https://bytekit.com/docs/sdk/typescript/fetch # Fetch Low-latency raw HTTP fetch with optional conversion. # Fetch Low-latency raw HTTP fetch with optional conversion. Accessed via `client.fetch`. ## create ```ts client.fetch.create(opts: FetchOpts, requestOptions?: RequestOptions): Promise<FetchResult> ``` POST /v1/fetch — fetch URL content via POST body. Resolves a `FetchResult` — the raw content body plus `X-Fetch-*` metadata. The body is returned verbatim — a JSON-serving upstream comes back as the raw JSON string, not a parsed object. `requestOptions` carries the CLIENT-side abort budget (`requestTimeoutMs`) and an optional caller `signal`. It matters most here: the gateway's own ceiling for a fetch is 60 s, so without a per-call budget a slow upstream holds the caller for the constructor default (2 minutes) rather than a duration the caller chose. ## get ```ts client.fetch.get(opts: FetchGetOpts, requestOptions?: RequestOptions): Promise<FetchResult> ``` GET /v1/fetch — fetch URL content via query params. Resolves a `FetchResult` — the raw content body plus `X-Fetch-*` metadata. Accepts only the five params the spec declares for this endpoint. `custom` (#2481) and the `markdown_*` / `with_*` tuning fields (#2539) are POST-body options with no GET query param — they were silently dropped by this builder, so they are now compile errors instead. Use `create()` (POST) for those. The query string is built by iterating `FETCH_GET_QUERY_KEYS`, the same list `FetchGetOpts` is derived from, so every accepted option is necessarily serialized. `requestOptions` carries the CLIENT-side abort budget (`requestTimeoutMs`) and an optional caller `signal`. As on `create`, the gateway's own ceiling for a fetch is 60 s, so a per-call budget is what keeps a slow upstream from holding the caller for the constructor default. ## Options (`FetchOpts`) - `url` (required) — `string` - `format` — `'markdown' | 'html'` — Output format. `'markdown'` runs the HTML→markdown pipeline; `'html'` returns raw HTML. Narrowed to the spec union — off-spec values (e.g. `'pdf'`) are rejected at compile time. - `country` — `string` - `timeout_ms` — `number` - `markdown_mode` — `'article' | 'raw' | 'llm'` — Markdown processing mode. Only used when `format=markdown`. article/raw/llm. - `markdown_query` — `string` — BM25 query string for relevance-ranked filtering. Only used when `format=markdown`. - `markdown_links` — `'inline' | 'references' | 'none' | 'text'` — Link rendering style. Only used when `format=markdown`. - `markdown_images` — `'inline' | 'references' | 'none' | 'text'` — Image retention mode. Only used when `format=markdown`. - `with_links_summary` — `boolean` — Append a `Links:` footer to the markdown output. Only used when `format=markdown`. - `with_images_summary` — `boolean` — Append an `Images:` footer to the markdown output. Only used when `format=markdown`. - `markdown_compact` — `boolean` — Compact whitespace output. Only used when `format=markdown`. - `markdown_filter_images` — `boolean` — Filter low-signal images. Only used when `format=markdown`. - `markdown_include_media` — `boolean` — When true, `formats.links`/`formats.images` return rich objects and a top-level `tables` array is included. Only used when `format=markdown`. - `markdown_include_warnings` — `boolean` — When true, enables markdown-pipeline and tag-filter warnings for affected html/markdown requests. - `markdown_include_stats` — `boolean` — When true, includes a top-level `stats` object (chars, tokens, blocks). Only used when `format=markdown`. - `cache_ttl` — `string | 0` — How long a freshly fetched URL may be served from cache. Matches the OpenAPI `FetchRequest.cache_ttl` schema: a duration string (`"48h"`, `"2d"`, up to `168h` / `7d`) or the integer `0` to disable caching. Defaults to `"48h"` server-side when omitted. - `custom` — `Record<string, unknown>` — User-supplied JSON payload, base64-encoded into the `X-Fetch-Custom` response header so callers can correlate the response to caller-side state. Capped at 4096 UTF-8 bytes after JSON serialization. Does NOT affect cache-key inputs. --- URL: https://bytekit.com/docs/sdk/typescript/monitors # Monitors Watch a URL on a schedule and webhook on change. # Monitors Watch a URL on a schedule and webhook on change. Accessed via `client.monitors`. ## create ```ts client.monitors.create(opts: MonitorCreateRequest, requestOptions?: RequestOptions): Promise<MonitorCreateResponse> ``` POST /v1/monitors — create a page-change monitor (screenshot or scrape capture). First-capture behavior is the same for both types: the first capture is a silent baseline (has_change=false, change_pct=null) with no change_detected webhook, whether the monitor is `scrape` or `screenshot`. From the second capture onward both types diff against the previous capture. ## list ```ts client.monitors.list(params?: ListMonitorsParams, requestOptions?: RequestOptions): Promise<ListMonitorsResponse> ``` GET /v1/monitors — list monitors for the account. `params` is optional and each key is omitted-when-absent, so `list()` with no argument serializes the bare `/v1/monitors` URL unchanged — existing zero-arg callers are unaffected. ## get ```ts client.monitors.get(id: MonitorId, requestOptions?: RequestOptions): Promise<MonitorResponse> ``` GET /v1/monitors/\{id\} — get a monitor by ID ## update ```ts client.monitors.update(id: MonitorId, opts: MonitorUpdateRequest, requestOptions?: RequestOptions): Promise<UpdateMonitorResponse> ``` PATCH /v1/monitors/\{id\} — update a monitor ## delete ```ts client.monitors.delete(id: MonitorId, requestOptions?: RequestOptions): Promise<void> ``` DELETE /v1/monitors/\{id\} — delete a monitor. Returns 204 No Content (`operations['deleteMonitor']['responses'][204]` declares `content?: never`), so there is no JSON arm to derive a payload from — hence `Promise<void>`. `client.request()` short-circuits the empty body to `undefined` before it would parse as JSON (#2450). ## captures ### list ```ts client.monitors.captures.list(id: MonitorId, requestOptions?: RequestOptions): Promise<ListMonitorCapturesResponse> ``` GET /v1/monitors/\{id\}/captures — list captures for a monitor --- URL: https://bytekit.com/docs/sdk/typescript/scrape-bulk # Scrape Bulk Create and poll bulk scrape jobs. # Scrape Bulk Create and poll bulk scrape jobs. Accessed via `client.scrape.bulk`. ## create ```ts client.scrape.bulk.create(opts: ScrapeBulkCreateRequest, requestOptions?: RequestOptions): Promise<ScrapeBulkCreateResponse> ``` POST /v1/scrape/bulk — create a bulk scrape job. `requestOptions` carries the CLIENT-side abort budget (`requestTimeoutMs`) and an optional caller `signal`; both override the constructor's `timeoutMs` default. ## get ```ts client.scrape.bulk.get(id: BulkId, requestOptions?: RequestOptions): Promise<ScrapeBulkGetResponse> ``` GET /v1/scrape/bulk/\{id\} — get a bulk scrape job --- URL: https://bytekit.com/docs/sdk/typescript/scrape # Scrape Fetch a URL as raw HTML, clean markdown, or structured content. # Scrape Fetch a URL as raw HTML, clean markdown, or structured content. Accessed via `client.scrape`. ## create ```ts client.scrape.create(opts: ScrapeOpts, requestOptions?: RequestOptions): Promise<ScrapeCreateResponse> ``` POST /v1/scrape — create a scrape job. Resolves to the 200 success envelope, or the 202 queued envelope when `async: true`. Narrow on `status` before reading the result. `requestOptions` carries the CLIENT-side abort budget (`requestTimeoutMs`) and an optional caller `signal`; both override the constructor's `timeoutMs` default. They are a different axis from `opts.timeout_ms`, which is the SERVER-side scrape budget. ## get ```ts client.scrape.get(id: ScrapeId, requestOptions?: RequestOptions): Promise<ScrapeGetResponse> ``` GET /v1/scrape/\{id\} — poll a scrape by ID KNOWN DRIFT (#2426): live staging spot-checks (two independent fetches) found the gateway serializes `billing_multiplier` as a JSON **string** (e.g. `"0.50"`) on this GET path, while the OpenAPI spec — and this derived `ScrapeGetResponse` type — declare it `number`. `POST /v1/scrape`'s success envelope for the same job serializes the identical field as a proper number, so the drift is specific to the GET path (likely a Postgres NUMERIC column round-tripping as a string on this query path). A consumer doing arithmetic on `billing_multiplier` after `get()` (e.g. `content_length * billing_multiplier`) should coerce with `Number(...)` defensively rather than trust the static type. This is a gateway/worker serialization bug, not a client bug — the fix belongs upstream in the gateway and is out of this ticket's scope. ## Options (`ScrapeOpts`) - `url` (required) — `string` - `formats` — `Array<'raw_html' | 'html' | 'markdown' | 'links' | 'images'>` - `country` — `string` - `cookies` — `ScrapeCookie[]` — Cookies to set before loading the page. `name` and `value` are required (spec `Cookie` schema). - `headers` — `Record<string, string>` - `delay_ms` — `number` - `timeout_ms` — `number` — SERVER-side scrape budget in ms — how long the gateway may spend on the scrape itself. For the CLIENT-side abort budget (when this SDK gives up on the wire), see `RequestOptions.requestTimeoutMs`. - `async` — `boolean` - `webhook_url` — `string` - `events` — `Array<'queued' | 'completed' | 'failed'>` - `markdown_mode` — `MarkdownMode` — Markdown processing mode. article=article extraction (default), raw=minimal cleanup, llm=compact LLM-optimised output. - `markdown_query` — `string` — BM25 query string for relevance-ranked content filtering. Omit or leave empty to disable. - `markdown_links` — `MarkdownLinks` — Link rendering style in the markdown output. - `markdown_images` — `MarkdownImages` — Image retention mode in the markdown output. Distinct from `markdown_filter_images` (which drops low-signal images before conversion). - `with_links_summary` — `boolean` — When true, appends a `Links:` footer to the markdown output. Automatically enabled when `markdown_links` is `'references'`. - `with_images_summary` — `boolean` — When true, appends an `Images:` footer to the markdown output. - `markdown_compact` — `boolean` — Collapse excessive whitespace for a more compact output. - `markdown_filter_images` — `boolean` — Filter low-signal images from the markdown output. - `markdown_include_media` — `boolean` — When true, `formats.links` and `formats.images` return `ScrapeScoredLink[]` / `ScrapeScoredImage[]` (rich objects) instead of `string[]`, and a top-level `tables` array is included. Only effective when `markdown` is in `formats`. - `markdown_include_warnings` — `boolean` — When true, the response includes markdown-pipeline and tag-filter warnings. `artifact_unavailable` may appear independently when a requested persisted format cannot be rehydrated. - `markdown_include_stats` — `boolean` — When true, the response includes a top-level `stats` object with `ScrapeStats` (chars, tokens, blocks). Only effective when `markdown` is in `formats`. - `token_budget` — `number` — Maximum token count for the markdown output. Truncates at the last paragraph → sentence → token boundary under the limit. Absent → no truncation. - `token_encoding` — `TokenEncoding` — Tokenizer encoding hint for `token_budget`. `cl100k_base` (default) matches GPT-4/4o; `o200k_base` matches GPT-4o-mini / o-series. - `mobile` — `boolean` — When true, emulate a mobile device (mobile UA + viewport). Default false (desktop). Part of the cache key, so mobile and desktop responses cache independently. - `remove_base64_images` — `boolean` — When true (default), base64-encoded `data:` image sources are stripped before markdown conversion and from `html` output. Set false to preserve them. Does not affect `raw_html`. - `include_tags` — `string[]` — HTML element names (not CSS selectors) to keep. When non-empty, only content within these elements is included in `html`/`markdown` output. Applied before `exclude_tags`. Does not affect `raw_html`. - `exclude_tags` — `string[]` — HTML element names (not CSS selectors) to strip from `html`/`markdown` output. Applied after `include_tags`. Does not affect `raw_html`. - `clean_markdown` — `boolean` — When true and `markdown` is in `formats`, the converted markdown is sent to Groq for cleaning before return. Graceful degradation on any Groq failure. Billing: 3× credits and bytes applied only when cleaning succeeds. - `cache_ttl` — `string | 0` — How long a freshly fetched URL may be served from cache. `'0'`/`0` skips the cache read (the result is still written), `'Nh'`/`'Nd'` set a TTL (capped at 168h / 7d). Default `'48h'`. Honoured on both the synchronous and asynchronous paths; the async success envelope reports the outcome via the `cache` field. - `custom` — `Record<string, unknown>` — User-supplied JSON payload, echoed back on the success envelope so callers can correlate the response to caller-side state (job IDs, batch metadata). Capped at 4096 UTF-8 bytes after JSON serialization. Does NOT affect cache-key inputs — two requests differing only in `custom` share the same cache slot. --- URL: https://bytekit.com/docs/sdk/typescript/screenshots # Screenshots Capture full-page or viewport screenshots. # Screenshots Capture full-page or viewport screenshots. Accessed via `client.screenshots`. ## create ```ts client.screenshots.create(opts: ScreenshotCreateRequest, params?: { async?: boolean }, requestOptions?: RequestOptions): Promise<ScreenshotCreateResponse> ``` POST /v1/screenshots — capture a screenshot. `params.async` maps to the `?async=true` query param (queued 202 capture). It is a QUERY param on `createScreenshot`, distinct from scrape where `async` is a body field. `params` is optional and `async` omitted-when-absent: `create(opts)` with no second argument posts to the bare `/v1/screenshots` URL unchanged — existing single-arg callers are unaffected. `requestOptions` is the THIRD positional parameter (after `params`) so both existing call shapes stay unaffected. It carries the CLIENT-side abort budget (`requestTimeoutMs`) and an optional caller `signal`; both override the constructor's `timeoutMs` default. ## get ```ts client.screenshots.get(id: ScreenshotId, requestOptions?: RequestOptions): Promise<ScreenshotGetResponse> ``` GET /v1/screenshots/\{id\} — poll a screenshot by ID. A terminal-`failed` screenshot returns the `Error` envelope on a 200 (issue #2027), so narrow the result before reading `image_url`. ## createWithResponse ```ts client.screenshots.createWithResponse(opts: ScreenshotCreateRequest, params?: { async?: boolean }, requestOptions?: RequestOptions): Promise<ApiResponse<ScreenshotCreateResponse>> ``` POST /v1/screenshots, returning the header set alongside the body (#2547). Same request as `screenshots.create` — same method, path, body and `RequestOptions` precedence — and `data` is exactly what `create()` resolves to. `create()`'s own return type is UNCHANGED; this is an additive sibling. The spec documents the `X-Screenshot-*` family (`ID`, `Status`, `URL`, `Credits-Charged`, `Duration-Ms`, `Created-At`, `Completed-At`, `Expires-At`, `Status-Code`, `Cache`, `Cache-Age`) plus `X-Raw-Bytes` / `X-Billable-Bytes` / `X-Billing-Multiplier`; every one of them is on `headers`. ## getWithResponse ```ts client.screenshots.getWithResponse(id: ScreenshotId, requestOptions?: RequestOptions): Promise<ApiResponse<ScreenshotGetResponse>> ``` GET /v1/screenshots/\{id\}, returning the header set alongside the body (#2547). Same request as `screenshots.get`, and `data` is exactly what `get()` resolves to (including the #2027 failed-envelope arm). `get()`'s own return type is UNCHANGED; this is an additive sibling. The documented `X-Screenshot-*`, `X-Raw-Bytes`, `X-Billable-Bytes` and `X-Billing-Multiplier` headers are on `headers`. --- URL: https://bytekit.com/docs/sdk/typescript/search # Search Run a web search and return ranked organic results. # Search Run a web search and return ranked organic results. Accessed via `client.search`. ## query ```ts client.search.query(opts: SearchOpts, requestOptions?: RequestOptions): Promise<SearchResponse> ``` POST /v1/search — run a web search and return ranked organic results. `requestOptions` carries the CLIENT-side abort budget (`requestTimeoutMs`) and an optional caller `signal`; both override the constructor's `timeoutMs` default. --- URL: https://bytekit.com/docs/sdk/typescript/sitemap # Sitemap Discover URLs from a domain's sitemap or by crawling. # Sitemap Discover URLs from a domain's sitemap or by crawling. Accessed via `client.sitemap`. ## create ```ts client.sitemap.create(opts: SitemapCreateRequest, requestOptions?: RequestOptions): Promise<SitemapCreateResponse> ``` POST /v1/sitemap — start a sitemap crawl. `requestOptions` carries the CLIENT-side abort budget (`requestTimeoutMs`) and an optional caller `signal`; both override the constructor's `timeoutMs` default. ## get ```ts client.sitemap.get(id: SitemapId, requestOptions?: RequestOptions): Promise<SitemapGetResponse> ``` GET /v1/sitemap/\{id\} — get a sitemap job by ID ## getWithResponse ```ts client.sitemap.getWithResponse(id: SitemapId, requestOptions?: RequestOptions): Promise<ApiResponse<SitemapGetResponse>> ``` GET /v1/sitemap/\{id\}, returning the header set alongside the body (#2547). Same request as `sitemap.get` — same method, path and `RequestOptions` precedence — and `data` is exactly what `get()` resolves to. `get()`'s own return type is UNCHANGED; this is an additive sibling, not a replacement. The spec documents `X-Sitemap-ID`, `X-Raw-Bytes`, `X-Billing-Multiplier` and `X-Billable-Bytes` on this endpoint's 200; every one of them is on `headers`. --- URL: https://bytekit.com/docs/sdk/typescript/usage # Usage Billing-period and daily usage breakdowns. # Usage Billing-period and daily usage breakdowns. Accessed via `client.usage`. ## get ```ts client.usage.get(requestOptions?: RequestOptions): Promise<GetUsageResponse> ``` GET /v1/usage — get current billing period usage. `requestOptions` carries the CLIENT-side abort budget (`requestTimeoutMs`) and an optional caller `signal`; both override the constructor's `timeoutMs` default. ## daily ```ts client.usage.daily(params?: GetUsageDailyParams, requestOptions?: RequestOptions): Promise<GetUsageDailyResponse> ``` GET /v1/usage/daily — get daily usage breakdown. Default range is the last 30 days; max range is 366 days. ## byEndpoint ```ts client.usage.byEndpoint(params?: GetUsageByEndpointParams, requestOptions?: RequestOptions): Promise<GetUsageByEndpointResponse> ``` GET /v1/usage/by-endpoint — get per-endpoint bandwidth breakdown. Default range is the last 30 days; max range is 366 days. --- URL: https://bytekit.com/docs/sdk/typescript/webhooks # Webhooks List and retry webhook deliveries. # Webhooks List and retry webhook deliveries. Accessed via `client.webhooks`. ## list ```ts client.webhooks.list(params?: ListWebhookDeliveriesParams, requestOptions?: RequestOptions): Promise<ListWebhookDeliveriesResponse> ``` GET /v1/webhook-deliveries — list webhook deliveries. `requestOptions` carries the CLIENT-side abort budget (`requestTimeoutMs`) and an optional caller `signal`; both override the constructor's `timeoutMs` default. ## retry ```ts client.webhooks.retry(id: WebhookDeliveryId, requestOptions?: RequestOptions): Promise<RetryWebhookDeliveryResponse> ``` POST /v1/webhook-deliveries/\{id\}/retry — requeue a failed or exhausted delivery.