Python SDK

Official Python client for ByteKit, with sync and async support.

For AI agents: https://bytekit.com/llms.txt — every docs page is available as markdown by appending .md

The official Python SDK is generated from the ByteKit OpenAPI spec, so its models and methods always match the API. ByteKit is a thin client on top of it for everyday use; the generated operations underneath ship in both synchronous and asynchronous form.

Install

pip install bytekit-sdk

The PyPI distribution is bytekit-sdk; the import name is bytekit.

Your first scrape

from bytekit import ByteKit

client = ByteKit(api_key="sk_live_your_api_key_here")

result = client.scrape.create(url="https://example.com", formats=["markdown"])
print(result.formats.markdown)

result is the parsed ScrapeSuccessEnvelope. Request fields are keyword arguments under the API's own field names, with plain JSON values, so formats=["markdown"] needs no enum import. Only the fields you pass are sent.

Handle errors

Any non-2xx response raises ByteKitError, as does a scrape.create call that comes back as HTTP 200 with a failed envelope. So does a response the generated operation cannot turn into a documented model: an undocumented status (even a 2xx), a body that is not JSON, or a body that does not match the documented shape. It carries the HTTP status_code and the API's code and message (both None when the body is not an error envelope):

import httpx
from bytekit import ByteKit, ByteKitError

client = ByteKit(api_key="sk_live_your_api_key_here")

try:
    client.scrape.create(url="https://example.com")
except ByteKitError as err:
    print(err.status_code, err.code, err.message)
except httpx.HTTPError:
    print("Could not reach the API")  # connection refused, DNS failure, timeout, ...

ByteKitError subclasses bytekit.errors.UnexpectedStatus, so code that already catches that keeps working. A transport failure is not wrapped: it propagates as the httpx exception.

Async scrapes

Pass async_=True to get a queued receipt back and poll it by ID. A 202 is returned, not raised:

queued = client.scrape.create(url="https://example.com", async_=True)
job = client.scrape.get(queued.id)

scrape.get returns the job's envelope as it stands, so a job that ended in failure comes back as a ScrapeErrorEnvelope rather than raising.

Other resources

Every API operation is a method on the client, grouped by resource. Path IDs are the first positional argument, query parameters are keywords (limit=5, status="completed"), and every other keyword is a request-body field. Pass body fields as keywords, not as a body= argument (that raises TypeError). A string for an enum-valued query parameter is converted, and one that is not a valid value raises ValueError.

ResourceMethods
accountget
bulkcreate, get, delete, list_screenshots
fetchcreate, get
fetch_bulkcreate, get
monitorscreate, list, get, update, delete, list_captures
scrapecreate, get
scrape_bulkcreate, get
screenshotscreate, get
searchcreate
sitemapcreate, get
usageget, get_daily, get_by_endpoint
webhookslist_deliveries, retry_delivery

Parameters keep the generated spelling: from_ on usage.get_daily and usage.get_by_endpoint, async_ on scrape.create and screenshots.create, and url_query (the url query parameter) on fetch.get. Date parameters take objects, not strings: datetime.date for from_ and to on the usage methods, datetime.datetime for since on webhooks.list_deliveries.

The generated client

ByteKit calls the generated operations in bytekit.api.*, which you can use directly when you want the typed request models and the full response:

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",
)

body = ScrapeRequest(
    url="https://example.com",
    formats=[ScrapeRequestFormatsItem.MARKDOWN],
)
response = create_scrape.sync(client=client, body=body)

# A documented error status (400, 429, 500, ...) comes back as an `Error` model, and a queued
# receipt as a ScrapeQueuedEnvelope. Only a ScrapeSuccessEnvelope carries populated formats.
if isinstance(response, ScrapeSuccessEnvelope) and isinstance(response.formats.markdown, str):
    print(response.formats.markdown)
else:
    print(f"Scrape not complete: {response}")

Each operation exposes four call styles:

  • create_scrape.sync(...) — returns the parsed model.
  • 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.

What a call does with a response it cannot turn into a documented model depends on the client's raise_on_unexpected_status, which defaults to True:

  • An undocumented status, a body that is not JSON or does not match the documented shape, or a 200 scrape carrying the failed envelope raises bytekit.errors.UnexpectedStatus (with status_code, code and message).
  • sync and asyncio return None for those only when you construct the client with raise_on_unexpected_status=False.
  • A transport failure never returns None: it raises the httpx exception.

Async usage

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())

Reference

The Python SDK Reference covers the generated client classes and operations by resource. ByteKit and ByteKitError are documented on this page.

Next steps