Skip to content

GET /api/v1/forecasts/:id/artifacts/:name

Streams a single artifact for the job through the API — callers do not receive direct storage URLs.

Use names exactly as listed in the artifacts array from GET /api/v1/forecasts/:id.

Call the endpoint

bash
JOB_ID="c7f2d8a9-3b4e-5f6a-7c8d-9e0f1a2b3c4d"
curl -sS -H "Authorization: Bearer $SYBILION_API_TOKEN" \
  "https://api.sybilion.dev/api/v1/forecasts/$JOB_ID/artifacts/forecast.json"
python
data = client.get_forecast_artifact(job_id, "forecast.json")
go
buf, err := c.GetForecastArtifact(context.Background(), jobID, "forecast.json")
if err != nil { log.Fatal(err) }
r
library(sybilion)

cl <- sybilion_client(token = Sys.getenv("SYBILION_API_TOKEN"))
cl$raw$ApiV1ForecastsIdArtifactsNameGet(
  job_id, "forecast.json", data_file = "forecast.json")
java
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(jobId, "forecast.json", Path.of("forecast.json"));

Artifact set

A successful forecast produces four files:

FileAlways present?Notes
forecast.jsonYesPoint + interval forecasts.
external_signals.jsonYesDriver / external-signal metadata.
backtest_trajectories.jsonOnly when backtest=trueFiltered to the last 12 months of trajectories — full history is intentionally truncated.
backtest_metrics.jsonOnly when backtest=trueAggregated metrics over rolling 6m / 12m / 24m / 60m windows.

Every file has the same envelope:

json
{ "version": "1.1", "data": {...} }

Treat version as a contract version; new fields may appear inside data at the same major version.

forecast.jsondata shape

json
{
  "forecast_series": {
    "2026-05-01": {
      "forecast": 1234.56,
      "quantile_forecast": { "0.1": 1100.0, "0.5": 1234.5, "0.9": 1380.7 }
    }
  },
  "forecast_horizon": 12,
  "forecast_start": "2026-05-01",
  "forecast_end": "2027-04-01"
}

quantile_forecast is present only for probabilistic runs.

external_signals.jsondata shape

A map of driver UUID → entry:

json
{
  "f0e1d2c3-...": {
    "driver_name": "EU industrial production index",
    "importance": {
      "horizon_1": { "0.0": 87.4, "1.0": 65.2 },
      "horizon_2": { "0.0": 80.1 },
      "overall":   { "mean": 73.5, "min": 41.0, "max": 87.4 }
    },
    "direction": {
      "horizon_0": { "0.0": 0.62 },
      "horizon_1": { "0.0": 0.58, "1.0": 0.41 },
      "overall":   { "mean": 0.55, "min": 0.41, "max": 0.62 }
    },
    "pearson_correlation": {
      "overall": { "mean": 0.47, "min": 0.31, "max": 0.59 },
      "lag_6":   0.59,
      "lag_12":  0.31
    }
  }
}

Per-entry fields: driver_name (human-readable label injected from the recommender), importance (per-horizon and overall normalized scores), direction (signed correlation per horizon and lag), pearson_correlation (per-lag and aggregated). Two fields are intentionally omitted to keep payloads small: normalized_series and granger_correlation.

backtest_metrics.jsondata shape

json
{
  "6m":  { "metrics": {...}, "tests": {}, "forecast_start": "...", "forecast_end": "..." },
  "12m": { "metrics": {...}, "tests": {}, "forecast_start": "...", "forecast_end": "..." },
  "24m": { "metrics": {...}, "tests": {}, "forecast_start": "...", "forecast_end": "..." },
  "60m": { "metrics": {...}, "tests": {}, "forecast_start": "...", "forecast_end": "..." }
}

Windows with no completed folds are omitted. metrics averages each named metric across folds; nested metrics are averaged per sub-key.

backtest_trajectories.jsondata shape

Array of trajectory objects (one per backtest split), sorted by forecast_start ascending:

json
[
  {
    "forecast_start": "2025-05-01",
    "forecast_end":   "2025-10-01",
    "metrics":        { "mape": 0.061, "rmse": 142.7 },
    "forecast_series": {
      "2025-05-01": { "actual": 1180.0, "forecast": 1163.4 },
      "2025-06-01": { "actual": 1212.5, "forecast": 1207.9 }
    }
  }
]

When backtest=true was submitted with a probabilistic run, each per-date entry has quantile_forecast instead of forecast (same shape as in forecast.json). actual is null if no observation existed for that date. Only trajectories whose forecast_start falls within the last 12 months of training data are included.

Common errors

CodeCauseWhat to do
401Missing or invalid bearer token.Check the API key.
404Job not found or artifact not available.Confirm job status is completed before downloading.
409Job has not completed yet.Poll GET /api/v1/forecasts/:id until status == "completed".
413Artifact exceeds the 100 MiB stream limit.Contact [email protected].

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