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 viaClient.rawfor 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
pip install sybilionRequires Python 3.10+.
Construct a client
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:
- Explicit
base_urlargument. SYBILION_API_BASE_URLenvironment variable.OPERATIONAL_API_BASE_URL(deprecated alias, kept for one transition release).- The compiled-in default that ships with the SDK (
sybilion.DEFAULT_PUBLIC_API_BASE_URL, currentlyhttps://api.sybilion.dev).
Trailing slashes are stripped. To verify the resolution at runtime:
from sybilion import resolve_api_base_url
print(resolve_api_base_url(None)) # what Client will useThe 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:
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
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}everypoll_sseconds. - Returns the response as soon as
settled == True— works forcompleted,failed, andcanceledjobs. - Raises
TimeoutErrorif 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.
# 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):
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)| Exception | HTTP | When |
|---|---|---|
BadRequestException | 400 | Malformed JSON, invalid query params. |
UnauthorizedException | 401 | Missing or invalid token. |
ForbiddenException | 403 | Authenticated but not allowed. |
NotFoundException | 404 | Unknown id, or outside the visibility window. |
ConflictException | 409 | Job not yet completed (artifact downloads). |
UnprocessableEntityException | 422 | Validation failure — body has error + one details[]. |
ServiceException | 5xx | Transient backend failure; retry with backoff. |
ApiException | other | Catch-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:
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
- Features — full use-case walkthroughs with Python tabs.
- Using curl and Go SDK.
- Package page:
sybilionon PyPI.