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, ergonomic methods, pagination iterators, exceptions).
  • sybilion._api — the OpenAPI-generated low-level client. Accessible via client._api as 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 ​

bash
pip install sybilion

Requires Python 3.10+.

Construct a client ​

python
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:

  1. Explicit base_url argument.
  2. SYBILION_API_BASE_URL environment variable.
  3. The compiled-in default (sybilion.DEFAULT_PUBLIC_API_BASE_URL, currently https://api.sybilion.dev).

To verify the resolution at runtime:

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

Wrapper methods ​

Client exposes a method for every endpoint:

MethodEndpoint
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 ​

python
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} 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.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.

python
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:

ParameterTypeNotes
metadataTimeseriesMetadataRequired. Title, description, keywords describing the series.
context_enrichedboolRequired. Pass True if the metadata was already enriched upstream; False to let the engine enrich it.
filtersFilters | NoneOptional. Scope by category / region ids and cap item count with limit.
date_fromstr | NoneOptional. Earliest date bound for alert events (YYYY-MM-DD).
date_tostr | NoneOptional. 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.

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 ​

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.

python
try:
    job = client.submit_forecast(body)
except Exception as exc:
    print("error:", exc)

When calling client._api.* directly, the generated client raises typed exceptions:

python
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)
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. 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:

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)