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, ergonomic methods, pagination iterators, exceptions).sybilion._api— the OpenAPI-generated low-level client. Accessible viaclient._apias an escape hatch for endpoints not yet wrapped.
This page documents the wrapper's idioms. For canonical use cases (forecasts, drivers, alerts, 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
# Token read from the environment automatically:
client = Client()
# Or pass it explicitly:
client = Client(token=os.environ["SYBILION_API_TOKEN"])
me = client.me()
print(me.user_id, me.available_eur_cents)When token is omitted, Client reads SYBILION_API_TOKEN from the environment. If neither is provided it raises ValueError. Client also accepts an optional base_url to override the default API origin.
Base URL resolution
Client(base_url=...) resolves the API origin in this order:
- Explicit
base_urlargument. SYBILION_API_BASE_URLenvironment variable.- The compiled-in default (
sybilion.DEFAULT_PUBLIC_API_BASE_URL, currentlyhttps://api.sybilion.dev).
To verify the resolution at runtime:
from sybilion import resolve_api_base_url
print(resolve_api_base_url(None)) # what Client will useWrapper methods
Client exposes a method for every endpoint:
| Method | Endpoint |
|---|---|
me() | GET /api/v1/me |
submit_forecast(request) | POST /api/v1/forecasts |
get_forecast(id) | GET /api/v1/forecasts/{id} |
get_forecast_artifact(id, name) | GET /api/v1/forecasts/{id}/artifacts/{name} |
wait_forecast(job_id, *, poll_s, timeout_s) | polling helper |
get_drivers(request) | POST /api/v1/drivers |
get_alerts(*, metadata, context_enriched, ...) | POST /api/v1/alerts |
list_jobs(*, page, limit, ...) | GET /api/v1/jobs |
iter_jobs_pages(*, limit, status, ...) | pagination helper |
get_usage(*, page, limit, ...) | GET /api/v1/usage |
iter_usage_pages(*, limit, ...) | pagination helper |
list_categories() | GET /api/v1/categories |
list_regions() | GET /api/v1/regions |
For endpoints not yet covered, the underlying generated client is accessible as client._api (a DefaultApi instance). Method names mirror the OpenAPI operationId: api_v1_me_get, api_v1_forecasts_post, etc.
wait_forecast — poll until settled
submit = client.submit_forecast(body)
job = client.wait_forecast(
submit.job_id,
poll_s=2.0, # seconds between polls
timeout_s=3600.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.get_forecast(id) in your own loop.
get_alerts — synchronous alert detection
client.get_alerts calls POST /api/v1/alerts and returns the result immediately — no polling required.
import os
from sybilion import Client
from sybilion._api.models import Filters, TimeseriesMetadata
client = Client(token=os.environ["SYBILION_API_TOKEN"])
meta = TimeseriesMetadata(
title="Brent Crude Oil Price Monthly",
description="Monthly average Brent crude oil spot price in USD/barrel.",
keywords=["oil", "brent", "energy", "commodity"],
)
filters = Filters(categories=[3, 7], regions=[42], limit=25)
alerts = client.get_alerts(
metadata=meta,
context_enriched=False,
filters=filters,
date_from="2024-01-01",
)
for alert in alerts:
print(alert["name"], alert["pct_change"], alert["trending"])Parameters:
| Parameter | Type | Notes |
|---|---|---|
metadata | TimeseriesMetadata | Required. Title, description, keywords describing the series. |
context_enriched | bool | Required. Pass True if the metadata was already enriched upstream; False to let the engine enrich it. |
filters | Filters | None | Optional. Scope by category / region ids and cap item count with limit. |
date_from | str | None | Optional. Earliest date bound for alert events (YYYY-MM-DD). |
date_to | str | None | Optional. Latest date bound for alert events (YYYY-MM-DD). |
Returns a list[dict] — each dict has name, pct_change, trending, and a news[] sub-list. See POST /api/v1/alerts for the full response shape.
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
Wrapper methods raise a plain Exception whose message is the error field from the API response body. If the body can't be parsed, the original ApiException from the generated client is re-raised.
try:
job = client.submit_forecast(body)
except Exception as exc:
print("error:", exc)When calling client._api.* directly, the generated client raises typed exceptions:
from sybilion import (
ApiException,
BadRequestException,
UnauthorizedException,
UnprocessableEntityException,
ServiceException,
)
try:
job = client._api.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. Inspect exc.status == 402 and parse exc.body for error text. Surface this as "top up balance" in your UI.
Custom configuration
The generated client uses urllib3 under the hood. To override the default timeout or connection settings, build a Configuration directly and access the generated API:
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.
- Alerts · Drivers — synchronous endpoints with SDK wrappers.
- Using curl · Go SDK · R SDK · Java SDK.
- Package page:
sybilionon PyPI.