Skip to content

Go SDK

The official Go SDK is published as go.sybilion.dev/sybilion. It ships two packages in one module:

  • sybilion at go.sybilion.dev/sybilion — the hand-written wrapper exposed to users (New, Options, Forecasts().Wait, ForEachUsagePage, AsGenericOpenAPIError, ...).
  • sybilionapi at go.sybilion.dev/sybilion/api — the OpenAPI-generated low-level client and models. Exposed via c.DefaultAPI() 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 Go tab.

Install

bash
go get go.sybilion.dev/sybilion@latest

Requires Go 1.25+.

Construct a client

go
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"go.sybilion.dev/sybilion"
)

func main() {
	c := sybilion.New(sybilion.Options{
		Token: os.Getenv("SYBILION_API_TOKEN"),
	})

	me, _, err := c.DefaultAPI().ApiV1MeGet(context.Background()).Execute()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(me.GetUserId(), me.GetAvailableEurCents())
}

sybilion.New is constructed from an Options struct. The wrapper does not read the token from the environment — pass it explicitly via Options.Token. The token can be an API key (sk_ops_...) or a dashboard session token; see Authentication.

Options fields

FieldDefaultNotes
TokenemptyBearer token. When empty the client makes unauthenticated requests (only /health works).
BaseURLresolved at runtimeAPI origin without trailing slash. See resolution rules below.
HTTPClient&http.Client{Timeout: 60 * time.Second}Inject your own for custom transports, retries, or different timeouts.
UserAgentgenerator defaultOverride to brand outgoing requests.

Base URL resolution

Options.BaseURL is resolved in this order:

  1. Explicit Options.BaseURL.
  2. SYBILION_API_BASE_URL environment variable (constant sybilion.EnvSybilionAPIBaseURL).
  3. OPERATIONAL_API_BASE_URL (deprecated alias kept for one transition release; constant sybilion.EnvOperationalAPIBaseURL).
  4. The compiled-in default sybilion.DefaultPublicAPIBaseURL (currently https://api.sybilion.dev).

Trailing slashes are stripped.

DefaultAPI() — escape hatch to the generated client

c.DefaultAPI() returns the *sybilionapi.DefaultAPIService for any endpoint not yet wrapped. Method names mirror the OpenAPI operationId: ApiV1MeGet, ApiV1ForecastsPost, ApiV1ForecastsIdGet, ApiV1RegionsGet, etc. The fluent builder returns (*Model, *http.Response, error) from Execute().

go
me, _, err := c.DefaultAPI().ApiV1MeGet(ctx).Execute()
tiers, _, err := c.DefaultAPI().ApiV1TiersGet(ctx).Execute()
regions, _, err := c.DefaultAPI().ApiV1RegionsGet(ctx).Execute()

If you need the raw configuration (custom defaults, header injection beyond UserAgent), c.Raw() returns the *sybilionapi.APIClient.

Forecasts().Wait — poll until settled

go
import "time"

ctx := context.Background()

acc, _, err := c.DefaultAPI().
	ApiV1ForecastsPost(ctx).
	ForecastRequestV1(*body).
	Execute()
if err != nil { log.Fatal(err) }

job, err := c.Forecasts().Wait(ctx, acc.GetJobId(), 2*time.Second)
if err != nil { log.Fatal(err) }

fmt.Println(job.GetStatus(), job.GetEurCentsFinal())
for _, a := range job.GetArtifacts() {
	fmt.Println(a.GetName(), a.GetSize())
}

Behaviour:

  • Polls GET /api/v1/forecasts/{id} every poll interval (no jitter, no backoff).
  • Returns the response as soon as settled == true — works for completed, failed, and canceled jobs.
  • Honors ctx: cancel the context to stop polling. The wrapper returns ctx.Err().

For a hard wall-clock cap, wrap the call in context.WithTimeout:

go
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()

job, err := c.Forecasts().Wait(ctx, acc.GetJobId(), 2*time.Second)

If you need backoff, jitter, or a max-attempts cap, write your own loop calling c.DefaultAPI().ApiV1ForecastsIdGet(ctx, id).Execute().

Error handling

The generated client returns *sybilionapi.GenericOpenAPIError for non-2xx responses with a decodable body. The wrapper exposes sybilion.AsGenericOpenAPIError(err) for ergonomic unwrapping (uses errors.As under the hood, so chained errors work):

go
import "go.sybilion.dev/sybilion"

acc, resp, err := c.DefaultAPI().
	ApiV1ForecastsPost(ctx).
	ForecastRequestV1(*body).
	Execute()

if err != nil {
	if ge, ok := sybilion.AsGenericOpenAPIError(err); ok {
		log.Printf("api error status=%d body=%s", resp.StatusCode, ge.Body())
		return
	}
	log.Fatal(err) // network / decode errors land here
}

_ = acc

The *http.Response returned alongside err carries the HTTP status code and headers — it's the most reliable way to branch on 402 / 422 / 429 etc., since the SDK doesn't expose typed exceptions per status code.

For a transport / decode-only fallback (when the body can't be parsed at all), the wrapper also defines sybilion.APIError{StatusCode, Body, Message} — used internally for early reads where the generator's helper isn't available. You won't see it on standard endpoints.

Custom HTTP client

Inject any *http.Client to replace timeouts, transport, retries, or proxies:

go
import (
	"crypto/tls"
	"net/http"
	"os"
	"time"

	"go.sybilion.dev/sybilion"
)

httpc := &http.Client{
	Timeout: 30 * time.Second,
	Transport: &http.Transport{
		TLSClientConfig:    &tls.Config{MinVersion: tls.VersionTLS12},
		MaxIdleConns:       50,
		IdleConnTimeout:    90 * time.Second,
	},
}

c := sybilion.New(sybilion.Options{
	Token:      os.Getenv("SYBILION_API_TOKEN"),
	HTTPClient: httpc,
	UserAgent:  "my-app/1.0",
})

For per-call cancellation and timeouts, use context.WithTimeout / context.WithCancel instead of mutating the *http.Client.

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 (go get go.sybilion.dev/[email protected]) for production builds.

See also

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