Skip to main content

bytekit-sdk

Official Python SDK for the ByteKit API.

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

Generated from the OpenAPI spec using openapi-python-client, filtered to the stable v0.1 operations.

Installation

pip install bytekit-sdk

The installed version is available as bytekit.__version__.

Quick start

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

# base_url defaults to https://api.bytekit.com, so only the token is required.
client = AuthenticatedClient(token="sk_live_your_api_key_here")

result = create_scrape.sync(
    client=client,
    body=ScrapeRequest(
        url="https://example.com",
        # Ask for markdown explicitly. `formats` is a list of enum members, not plain
        # strings. Omit it and the server returns raw HTML instead, leaving
        # `formats.markdown` unset.
        formats=[ScrapeRequestFormatsItem.MARKDOWN],
    ),
)

if isinstance(result, ScrapeSuccessEnvelope):
    print(result.formats.markdown)

See Error handling for what result can be when the request fails.

Error handling

The SDK has a two-tier error contract. Both tiers are safe: an operation never raises a bare json.JSONDecodeError or a bare KeyError, even when a server returns an HTML error page or a payload whose shape has drifted from the spec.

Situation What you get
A status the OpenAPI spec documents for that operation (e.g. a 422 or 500 on create_scrape), with a JSON body of the documented shape The typed Error model is returned, not raised. Check with isinstance(result, Error) and read result.error.code / result.error.message.
An undocumented status, or a non-JSON body on a documented status (HTML error page, load-balancer text, empty body) errors.UnexpectedStatus is raised, carrying .status_code and the raw .content. When the body is the documented Error envelope, .code / .message are populated too.
A documented status whose JSON body does not match the documented schema — a field the server renamed, dropped or retyped errors.UnexpectedStatus is raised, same shape as the row above. The underlying parse failure (e.g. KeyError: 'schema_version') is attached as __cause__ for diagnosis.
Any of the above, with raise_on_unexpected_status=False Nothing is raised; the operation returns None.

The table describes the generated operations (create_scrape.sync, get_usage.sync, …), whose return types are Optional[...] accordingly. The hand-written AuthenticatedClient.search(...) convenience method is deliberately outside it: it returns a non-Optional CreateSearchResponse200 and raises errors.UnexpectedStatus on anything else — including documented error statuses, and including a malformed 200 — in both raise modes. raise_on_unexpected_status does not apply to it.

from bytekit import AuthenticatedClient
from bytekit.api.scrape import create_scrape
from bytekit.errors import UnexpectedStatus
from bytekit.models.error import Error
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(token="sk_live_your_api_key_here")

try:
    result = create_scrape.sync(
        client=client,
        body=ScrapeRequest(
            url="https://example.com",
            # Ask for markdown explicitly — omit `formats` and the server returns raw HTML,
            # leaving `formats.markdown` unset.
            formats=[ScrapeRequestFormatsItem.MARKDOWN],
        ),
    )
except UnexpectedStatus as err:
    # Undocumented status, or a non-JSON body on a documented one. Never a JSONDecodeError.
    print(f"request failed with status {err.status_code}: {err.code} {err.message}")
else:
    if isinstance(result, Error):
        # Documented 4xx/5xx with a JSON body: RETURNED as a typed model, not raised.
        print(f"api error {result.error.code}: {result.error.message}")
    elif isinstance(result, ScrapeSuccessEnvelope):
        print(result.formats.markdown)
    else:
        # 202 ScrapeQueuedEnvelope — poll get_scrape with this id until it completes.
        print(f"queued as {result.id}")

Transport errors

Both tiers above describe what happens once the server has answered. When it never does, the underlying httpx exception propagates unchanged — the SDK catches neither, on every generated operation and on search(...) alike:

Exception When
httpx.TimeoutException The request was sent and did not finish within the client's timeout (120s by default).
httpx.ConnectError The connection was never established: DNS failure, connection refused, TLS handshake failure.

raise_on_unexpected_status does not apply to either — it governs how a response is handled, and there is no response in these cases. Catch them alongside errors.UnexpectedStatus:

except (UnexpectedStatus, httpx.TimeoutException, httpx.ConnectError) as err: ...

Defaults

The client ships with production-ready defaults so AuthenticatedClient(token=...) works out of the box:

Setting Default Notes
base_url https://api.bytekit.com Pass base_url= to target staging or a proxy.
timeout 120s (httpx.Timeout(120.0)) Finite by default — requests no longer hang indefinitely. Pass timeout= to override.
raise_on_unexpected_status True Undocumented statuses raise errors.UnexpectedStatus instead of silently returning None. Pass raise_on_unexpected_status=False to restore the old opt-out.

Explicit constructor arguments always win over these defaults (explicit arg > default).

Every constructor argument is keyword-only

AuthenticatedClient accepts no positional arguments. token, base_url, prefix, auth_header_name and the rest are all passed by name:

client = AuthenticatedClient(base_url="https://api.bytekit.com", token="sk_live_your_api_key_here")

Migrating from a pre-0.3.0 positional call. base_url used to be the first positional argument, so AuthenticatedClient("https://…", "sk_live_…") was a documented call. Once base_url became a defaulted keyword argument, that same call silently bound the URL to token and the API key to prefix — producing an Authorization: sk_live_… https://… header sent to the default host, i.e. a wrong credential against production with nothing raised. Since 0.3.5 it raises TypeError instead. Add the keywords; nothing else changes.

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

Clients and event loops

An httpx.AsyncClient — and therefore its connection pool — belongs to the event loop that created it. The recommended shape is a context manager, which scopes the client to exactly one loop:

async def main():
    async with AuthenticatedClient(token="sk_live_your_api_key_here") as client:
        ...

Two rules cover everything else:

  • A client the SDK builds for you is rebuilt automatically. If you reuse one client across several asyncio.run(...) calls, the SDK notices the running loop has changed and transparently replaces its internal AsyncClient. Your base_url, headers, timeout, httpx_args and authentication are all re-applied, so this is invisible apart from a new connection. Calling get_async_httpx_client() outside any running loop returns the cached client unchanged.
  • A client you pass to set_async_httpx_client(...) is yours. The SDK never rebuilds or closes it, so a client you supply must be created and used on the same loop — that is the one case where crossing loops is still your responsibility.

Available operations

Module Method Description
api.scrape create_scrape, get_scrape Web content extraction
api.screenshots create_screenshot, get_screenshot Page screenshots
api.bulk create_bulk, get_bulk, delete_bulk, list_bulk_screenshots Bulk screenshot jobs
api.scrape_bulk create_scrape_bulk, get_scrape_bulk Bulk scrape jobs
api.fetch get_fetch, post_fetch Raw HTTP fetch
api.fetch_bulk create_fetch_bulk, get_fetch_bulk Bulk fetch jobs
api.monitors create_monitor, list_monitors, get_monitor, update_monitor, delete_monitor, list_monitor_captures Page-change monitors (screenshot + scrape)
api.sitemap create_sitemap, get_sitemap Sitemap crawl
api.search create_search (or the synchronous AuthenticatedClient.search(...); use create_search.asyncio in async code) Web search
api.usage get_usage, get_usage_daily, get_usage_by_endpoint Account usage & billing
api.webhooks list_webhook_deliveries, retry_webhook_delivery Webhook delivery log & retry
api.account get_account Account details

Two things worth knowing before your first call

get_fetch takes url_query=, not url=. The URL to fetch is passed as url_query; it travels as the url query parameter on the wire. The rename comes from the generator, which avoids reusing url for an operation argument. get_fetch.sync(client=client, url="…") is a TypeError:

from bytekit import AuthenticatedClient
from bytekit.api.fetch import get_fetch

client = AuthenticatedClient(token="sk_live_your_api_key_here")
result = get_fetch.sync(client=client, url_query="https://example.com")

AuthenticatedClient.search(...) is synchronous. Calling it from inside a coroutine blocks the event loop for the duration of the request — nothing raises, so it is easy to miss. In async code use the generated operation instead, which is a real coroutine:

from bytekit.api.search import create_search
from bytekit.models.create_search_body import CreateSearchBody

response = await create_search.asyncio(client=client, body=CreateSearchBody(query="climate tech"))

The convenience method's extra behavior — raising errors.UnexpectedStatus on every non-200 rather than returning a typed Error — is its own, and create_search.asyncio follows the two-tier contract described under Error handling like every other generated operation.

License

MIT — see LICENSE.

Release files for bytekit-sdk 0.5.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bytekit-sdk 0.5.1
File Size Uploaded
bytekit_sdk-0.5.1.tar.gz 120.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bytekit-sdk 0.5.1
File Interpreter ABI Platform
bytekit_sdk-0.5.1-py3-none-any.whl Python 3 none any Details

Total release size: 364.6 kB

Release files / bytekit_sdk-0.5.1.tar.gz

Download URL bytekit_sdk-0.5.1.tar.gz
Size 120.7 kB
Tags Source
SHA-256 checksum
How to use checksums
b5415f14c4956cb0c7226c8e8c9fca2e5b7bbadc49adfb759d222b7d024c6ec0
BLAKE2b-256 checksum
How to use checksums
f738abb46c7fad4d37de73fe054d02ac7150e272e23632fa656bdc411d5fb38d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / bytekit_sdk-0.5.1-py3-none-any.whl

Download URL bytekit_sdk-0.5.1-py3-none-any.whl
Size 243.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a00d763a35079a71ad33305e6eac008260d6b4ad02ee8a3b880c37ae8c3e800f
BLAKE2b-256 checksum
How to use checksums
182ffe12ca21b2fc33ec5074ccad946a3a236792fc8abcff859cd7286e9b8738
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release history Release notifications | RSS feed

0.9.0

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.3

2 release files

0.5.2

2 release files

This release

0.5.1 This release

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.9

2 release files

0.3.8

2 release files

0.3.6

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.2

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page