Skip to content

Quickstart

This page helps you build a complete forecast job using the Sybilion API. All you need is a Sybilion account to get started. You can use Sybilion in three ways — direct API calls (curl or any HTTP client), our Python, Go, R, and Java SDKs, or via MCP to connect AI assistants like Claude or ChatGPT.

1. Sign up or sign in

Create a new account or sign in at the Developers Portal:

Open Developers Portal →

2. Create an API key

In the Developers Portal, navigate to API keys and create a new key.

Important: The full secret is shown only once, at creation time. Copy it immediately — it starts with sk_ops_… and cannot be retrieved again.

The examples in this guide all read from $SYBILION_API_TOKEN. Set it in your shell/terminal using this command:

bash
export SYBILION_API_TOKEN="sk_ops_..."

3. Pick a client

The base URL is https://api.sybilion.dev. Choose the client that fits your stack:

bash
# Already on most systems. To install if needed:
# macOS:           brew install curl
# Debian/Ubuntu:   sudo apt install -y curl
# Windows:         winget install curl.curl
bash
pip install sybilion
bash
go get go.sybilion.dev/sybilion@latest
r
install.packages("sybilion")
xml
<!-- Maven (pom.xml) -->
<dependency>
  <groupId>dev.sybilion</groupId>
  <artifactId>sybilion</artifactId>
</dependency>

SDK packages: Python — sybilion on PyPI · Go — go.sybilion.dev/sybilion on pkg.go.dev · R — sybilion on CRAN · Java — dev.sybilion:sybilion on Maven Central.

4. Verify authentication — GET /api/v1/me

Call GET /api/v1/me to confirm your key works and see your current balance and tier.

bash
curl -sS \
  -H "Authorization: Bearer $SYBILION_API_TOKEN" \
  https://api.sybilion.dev/api/v1/me
python
import os
from sybilion import Client

c = Client(token=os.environ["SYBILION_API_TOKEN"])
me = c.me()
print(me.user_id, me.available_eur_cents, me.api_usage_tier)
go
package main

import (
  "context"
  "fmt"
  "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 { panic(err) }
  fmt.Println(me.GetUserId(), me.GetAvailableEurCents(), me.GetApiUsageTier())
}
r
library(sybilion)

cl <- sybilion_client(token = Sys.getenv("SYBILION_API_TOKEN"))
me <- cl$raw$ApiV1MeGet()$content
cat(me$user_id, me$available_eur_cents, me$api_usage_tier, "\n")
java
import com.sybilion.Client;
import com.sybilion.Options;

Client c = new Client(Options.builder().token(System.getenv("SYBILION_API_TOKEN")).build());
var me = c.defaultApi().apiV1MeGet();
System.out.println(me.getUserId() + " " + me.getAvailableEurCents() + " " + me.getApiUsageTier());

A successful response looks like this:

json
{
  "user_id": "1f2a8b3e-4c5d-46d7-9a01-2b3c4d5e6f70",
  "balance_eur_cents": 1234,
  "available_eur_cents": 1134,
  "api_usage_tier": 1
}
  • available_eur_cents is your spendable balance (balance minus any active holds). Monetary values are always integer EUR cents (100 = €1.00).
  • api_usage_tier is your current pricing tier.
  • A 401 means your token is missing or invalid — double-check the value you exported.

See GET /api/v1/me for the full field references.

5. Submit a forecast — POST /api/v1/forecasts

Save the body below as forecast_body.json. This example uses the shortest possible horizon (soft_horizon: 1), which needs a minimum of 40 monthly observations. Your most recent data point cannot be older than 12 months. Replace the timeseries values with your own data.

Date format: Each key must be the first day of the month in YYYY-MM-DD format (e.g. 2024-06-01, not 2024-06-15). Any other day-of-month will be rejected.

json
{
  "pipeline_version": "v1",
  "frequency": "monthly",
  "soft_horizon": 1,
  "recency_factor": 0.5,
  "timeseries_metadata": {
    "title": "Monthly Widget Sales Europe",
    "description": "Monthly unit sales of widgets in the European market.",
    "keywords": ["widget", "sales", "europe", "consumer goods", "retail"]
  },
  "timeseries": {
    "2021-12-01": 218.5,
    "2022-01-01": 148.1,
    "2022-02-01": 145.9,
    "2022-03-01": 162.4,
    "2022-04-01": 168.7,
    "2022-05-01": 166.2,
    "2022-06-01": 178.5,
    "2022-07-01": 172.3,
    "2022-08-01": 168.9,
    "2022-09-01": 181.4,
    "2022-10-01": 189.7,
    "2022-11-01": 204.8,
    "2022-12-01": 218.5,
    "2023-01-01": 155.3,
    "2023-02-01": 152.7,
    "2023-03-01": 170.1,
    "2023-04-01": 176.4,
    "2023-05-01": 174.0,
    "2023-06-01": 186.2,
    "2023-07-01": 180.5,
    "2023-08-01": 177.1,
    "2023-09-01": 190.3,
    "2023-10-01": 198.4,
    "2023-11-01": 213.9,
    "2023-12-01": 227.6,
    "2024-01-01": 162.8,
    "2024-02-01": 160.2,
    "2024-03-01": 178.5,
    "2024-04-01": 184.9,
    "2024-05-01": 182.3,
    "2024-06-01": 194.7,
    "2024-07-01": 188.4,
    "2024-08-01": 185.0,
    "2024-09-01": 198.6,
    "2024-10-01": 207.3,
    "2024-11-01": 223.1,
    "2024-12-01": 237.8,
    "2025-01-01": 170.4,
    "2025-02-01": 168.1,
    "2025-03-01": 186.7
  }
}

Then submit it:

bash
curl -sS -X POST https://api.sybilion.dev/api/v1/forecasts \
  -H "Authorization: Bearer $SYBILION_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d @forecast_body.json
python
import json
import os

from sybilion import Client

c = Client(token=os.environ["SYBILION_API_TOKEN"])
with open("forecast_body.json", encoding="utf-8") as f:
    body = json.load(f)
resp = c.submit_forecast(body)
print(resp.job_id, resp.poll_url)
go
package main

import (
  "context"
  "encoding/json"
  "fmt"
  "log"
  "os"

  "go.sybilion.dev/sybilion"
  api "go.sybilion.dev/sybilion/api"
)

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

  data, err := os.ReadFile("forecast_body.json")
  if err != nil { log.Fatal(err) }
  var body api.ForecastRequestV1
  if err := json.Unmarshal(data, &body); err != nil { log.Fatal(err) }

  resp, err := c.SubmitForecast(context.Background(), body)
  if err != nil { log.Fatal(err) }
  fmt.Println(resp.GetJobId(), resp.GetPollUrl())
}
r
library(jsonlite)
library(sybilion)

cl <- sybilion_client(token = Sys.getenv("SYBILION_API_TOKEN"))
payload <- jsonlite::fromJSON("forecast_body.json")
req <- ForecastRequestV1$new(
  frequency           = payload$frequency,
  pipeline_version    = payload$pipeline_version,
  recency_factor      = payload$recency_factor,
  timeseries          = payload$timeseries,
  timeseries_metadata = TimeseriesMetadata$new(
    title       = payload$timeseries_metadata$title,
    description = payload$timeseries_metadata$description,
    keywords    = payload$timeseries_metadata$keywords
  ),
  backtest     = payload$backtest,
  soft_horizon = payload$soft_horizon
)
started <- cl$raw$ApiV1ForecastsPost(req)
cat(started$job_id, started$poll_url, "\n")
java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sybilion.Client;
import com.sybilion.Options;
import com.sybilion.generated.model.ForecastRequestV1;
import java.nio.file.Files;
import java.nio.file.Path;

Client c = new Client(Options.builder().token(System.getenv("SYBILION_API_TOKEN")).build());
ObjectMapper om = new ObjectMapper().findAndRegisterModules();
ForecastRequestV1 req = om.readValue(
    Files.readString(Path.of("forecast_body.json")), ForecastRequestV1.class);
var resp = c.defaultApi().apiV1ForecastsPost(req);
System.out.println(resp.getJobId() + " " + resp.getPollUrl());

A successful response is 202 Accepted:

json
{
  "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "poll_url": "/api/v1/forecasts/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

Copy the job_id — you'll need it in the next step. See POST /api/v1/forecasts for the full field reference and validation rules.

6. Poll until completed

Forecasts run asynchronously and typically take a few minutes. Keep checking the job status until settled is true.

bash
JOB_ID="<paste-job-id>"
API="https://api.sybilion.dev"
until curl -sS -H "Authorization: Bearer $SYBILION_API_TOKEN" \
  "$API/api/v1/forecasts/$JOB_ID" | grep -qE '"settled"[[:space:]]*:[[:space:]]*true'; do
  sleep 2
done
curl -sS -H "Authorization: Bearer $SYBILION_API_TOKEN" \
  "$API/api/v1/forecasts/$JOB_ID"
python
import os
from sybilion import Client

c = Client(token=os.environ["SYBILION_API_TOKEN"])
job = c.wait_forecast("<paste-job-id>", poll_s=2.0, timeout_s=3600.0)
print(job.status, job.artifacts)
go
package main

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

  "go.sybilion.dev/sybilion"
)

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

  jobID := "<paste-job-id>"
  job, err := c.WaitForecast(context.Background(), jobID, 2*time.Second)
  if err != nil { log.Fatal(err) }
  fmt.Println(job.GetStatus(), job.GetEurCentsFinal())
  for _, a := range job.GetArtifacts() {
    fmt.Println(" -", a.GetName())
  }
}
r
library(sybilion)

cl <- sybilion_client(token = Sys.getenv("SYBILION_API_TOKEN"))
job <- cl$wait_forecast("<paste-job-id>", poll_s = 2.0, timeout_s = 3600.0)
cat(job$status, "\n")
for (a in job$artifacts) cat(" -", a$name, "\n")
java
import com.sybilion.Client;
import com.sybilion.Options;
import java.time.Duration;

Client c = new Client(Options.builder().token(System.getenv("SYBILION_API_TOKEN")).build());
var job = c.forecasts().waitUntilSettled(
    "<paste-job-id>", Duration.ofSeconds(2), Duration.ofHours(1));
System.out.println(job.getStatus() + " " + job.getEurCentsFinal());
for (var a : job.getArtifacts()) System.out.println(" - " + a.getName());

Once status is completed, the response includes how much the forecast cost and an artifacts array listing the files ready to download:

json
{
  "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "completed",
  "settled": true,
  "eur_cents_final": 3,
  "artifacts": [
    { "name": "forecast.json",          "href": "/api/v1/forecasts/a1b2c3d4-.../artifacts/forecast.json" },
    { "name": "external_signals.json",  "href": "/api/v1/forecasts/a1b2c3d4-.../artifacts/external_signals.json" }
  ]
}

See GET /api/v1/forecasts/:id for the full response shape.

7. Fetch artifacts

Download the forecast results using the artifact names from the artifacts array above.

bash
curl -sS \
  -H "Authorization: Bearer $SYBILION_API_TOKEN" \
  "https://api.sybilion.dev/api/v1/forecasts/$JOB_ID/artifacts/forecast.json"
python
import json
import os
from sybilion import Client

c = Client(token=os.environ["SYBILION_API_TOKEN"])
data = c.get_forecast_artifact("<paste-job-id>", "forecast.json")
forecast = json.loads(data)
print(forecast["data"]["forecast_series"])
go
package main

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

  "go.sybilion.dev/sybilion"
)

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

  f, err := c.GetForecastArtifact(context.Background(), "<paste-job-id>", "forecast.json")
  if err != nil { log.Fatal(err) }
  defer f.Close()
  body, _ := io.ReadAll(f)
  fmt.Println(string(body))
}
r
library(jsonlite)
library(sybilion)

cl <- sybilion_client(token = Sys.getenv("SYBILION_API_TOKEN"))
cl$raw$ApiV1ForecastsIdArtifactsNameGet(
  "<paste-job-id>", "forecast.json", data_file = "forecast.json")
forecast <- jsonlite::fromJSON("forecast.json")
print(forecast$data$forecast_series)
java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sybilion.Client;
import com.sybilion.Options;
import java.nio.file.Path;

Client c = new Client(Options.builder().token(System.getenv("SYBILION_API_TOKEN")).build());
c.forecasts().downloadArtifactToFile(
    "<paste-job-id>", "forecast.json", Path.of("forecast.json"));
var tree = new ObjectMapper().readTree(Path.of("forecast.json").toFile());
System.out.println(tree.get("data").get("forecast_series"));

The forecast.json artifact looks like this (our example used soft_horizon: 1, so there is one forecast point):

json
{
  "version": "1.1",
  "data": {
    "forecast_horizon": 1,
    "forecast_start": "2025-04-01",
    "forecast_end": "2025-04-01",
    "forecast_series": {
      "2025-04-01": {
        "forecast": 191.3
      }
    }
  }
}

The external_signals.json artifact is always present alongside forecast.json and contains the ranked external drivers (macroeconomic indicators, regional and category signals) that influenced the forecast. See Artifact download for the full schema of all artifact files.

Next steps

Explore more features

  • Drivers — find the external signals that impact your series, without running a full forecast.
  • Alerts — get info about macroeconomic events relevant to your data.
  • Regions & categories — browse the dimension catalog to narrow your results.
  • Account & usage — check your balance, tier, and charge history.

Go deeper on forecasts

Connect and integrate

Get help

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