Skip to content

Python SDK

The official Python SDK is published as sybilion on PyPI. It ships two layers in one package:

  • sybilion — the hand-written wrapper exposed to users (Client, wait_forecast, iter_*_pages, exceptions).
  • sybilion._api — the OpenAPI-generated low-level client. Exposed via Client.raw for endpoints not yet wrapped.

This page documents the wrapper's idioms. For canonical use cases (forecasts, drivers, account, regions/categories), see the Features section — every example there has a Python tab.

Install

bash
pip install sybilion

Requires Python 3.10+.

Construct a client

python
import os

from sybilion import Client

client = Client(token=os.environ["SYBILION_API_TOKEN"])
me = client.raw.api_v1_me_get()
print(me.user_id, me.available_eur_cents)

Client takes a Bearer token and an optional base_url. The constructor does not read environment variables for the token — pass it explicitly. The token can be an API key (sk_ops_...) or a dashboard session token; see Authentication.

Base URL resolution

Client(token=..., base_url=...) resolves the API origin in this order:

  1. Explicit base_url argument.
  2. SYBILION_API_BASE_URL environment variable.
  3. OPERATIONAL_API_BASE_URL (deprecated alias, kept for one transition release).
  4. The compiled-in default that ships with the SDK (sybilion.DEFAULT_PUBLIC_API_BASE_URL, currently https://api.sybilion.dev).

Trailing slashes are stripped. To verify the resolution at runtime:

python
from sybilion import resolve_api_base_url
print(resolve_api_base_url(None))  # what Client will use

The raw property — escape hatch to the generated API

client.raw returns the underlying DefaultApi instance from sybilion._api. Use it for any endpoint not yet covered by a wrapper helper:

python
me = client.raw.api_v1_me_get()
tiers = client.raw.api_v1_tiers_get()
regions = client.raw.api_v1_regions_get()

Method names mirror the OpenAPI operationId, lowercased and snake-cased: api_v1_me_get, api_v1_forecasts_post, api_v1_forecasts_id_get, api_v1_jobs_get, etc.

wait_forecast — poll until settled

python
submit = client.raw.api_v1_forecasts_post(forecast_request_v1=body)

job = client.wait_forecast(
    submit.job_id,
    poll_s=2.0,        # seconds between polls
    timeout_s=600.0,   # max total wait
)

print(job.status, job.eur_cents_final)
for art in job.artifacts or []:
    print(art.name, art.size)

Behaviour:

  • Polls GET /api/v1/forecasts/{id} every poll_s seconds.
  • Returns the response as soon as settled == True — works for completed, failed, and canceled jobs.
  • Raises TimeoutError if the deadline is exceeded; the job continues running on the server and you can resume polling later.

Pick poll_s based on your latency tolerance — 2–5 seconds is typical. The wrapper does not back off; if you need exponential backoff, call client.raw.api_v1_forecasts_id_get(id=...) in your own loop.

Pagination iterators

Two helpers walk paginated endpoints page-by-page so you don't have to track page / total_pages yourself.

python
# All usage events (newest first by default).
for page in client.iter_usage_pages(limit=100, sort="created_at", order="desc"):
    for ev in page.usage_events:
        print(ev.id, ev.eur_cents_charged, ev.created_at)

# Only completed forecast jobs.
for page in client.iter_jobs_pages(limit=100, status="completed"):
    for job in page.jobs:
        print(job.job_id, job.status, job.eur_cents_final)

Each iterator stops automatically when page == pagination.total_pages. Stop early with break.

Error handling

The generated client raises subclasses of sybilion.ApiException on 4xx / 5xx (the same classes are also exported from the top-level sybilion package for convenience):

python
from sybilion import (
    ApiException,
    BadRequestException,
    UnauthorizedException,
    NotFoundException,
    ConflictException,
    UnprocessableEntityException,
    ServiceException,
)

try:
    submit = client.raw.api_v1_forecasts_post(forecast_request_v1=body)
except UnprocessableEntityException as exc:
    # 422 — single validation detail in exc.body
    print("validation failed:", exc.body)
except UnauthorizedException:
    print("token rejected — refresh SYBILION_API_TOKEN")
except ApiException as exc:
    print("HTTP", exc.status, exc.reason, exc.body)
ExceptionHTTPWhen
BadRequestException400Malformed JSON, invalid query params.
UnauthorizedException401Missing or invalid token.
ForbiddenException403Authenticated but not allowed.
NotFoundException404Unknown id, or outside the visibility window.
ConflictException409Job not yet completed (artifact downloads).
UnprocessableEntityException422Validation failure — body has error + one details[].
ServiceException5xxTransient backend failure; retry with backoff.
ApiExceptionotherCatch-all base class.

Useful attributes on every ApiException: status (int), reason (str), body (raw bytes), headers (dict).

For a 402 (insufficient balance), the SDK raises ApiException (not a dedicated subclass). Inspect exc.status == 402 and parse exc.body for error text. Surface this as "top up balance" in your UI.

Custom HTTP client / timeout

The generated client uses urllib3 under the hood. To override the default timeout or connection settings, build a Configuration directly and skip Client:

python
import os

from sybilion import resolve_api_base_url
from sybilion._api import ApiClient, Configuration, DefaultApi

cfg = Configuration(
    host=resolve_api_base_url(None),
    access_token=os.environ["SYBILION_API_TOKEN"],
)
cfg.retries = 3                   # urllib3 retry policy
cfg.connection_pool_maxsize = 10  # parallel calls

raw_api = DefaultApi(ApiClient(cfg))
me = raw_api.api_v1_me_get()

Per-call timeouts can be passed via the underlying urllib3 request — see the urllib3 docs for the full surface.

Versioning

The SDK uses SemVer independently of the API server version; minor releases stay backward-compatible, breaking changes bump the major. Pin to a tag (sybilion==0.1.0) for production builds.

See also

[email protected] · Slack · Discord (links in Community page & header icons)