Forecast API: time series forecasting with Mistral's Timestral on Opper

By Mattias Lundell -

TL;DR: Send a series' history to POST https://api.opper.ai/v3/forecast with your Opper API key and a model, timestral-5m to timestral-209m, and get back a median and quantiles for every future step. No training, no GPU on your side. Opper runs all four sizes in Stockholm and bills per time step: a day of 15-minute forecasts from three weeks of history costs USD 0.000149 on the largest model. On 28 days of German electricity load every size cut the error of a same-time-last-week forecast by about 40%.

Mistral released Timestral on 15 October 2026: its first time-series foundation models, in four sizes from 5 million to 209 million parameters. You give one a series' history and it returns a probabilistic forecast, zero-shot, with no training on your data. Mistral published the weights and the inference code, and its model page lists no API model id or price. Opper serves all four behind one endpoint.

This post covers the request, a test on four weeks of real grid data (load and day-ahead prices), covariates, what a forecast costs and where it runs.

What Timestral is

  • Four sizes, one architecture: 5M, 28M, 123M and 209M parameters. Mistral reports the 209M model first among zero-shot models on GIFT-Eval, TIME and fev-bench, and every size on the accuracy-for-size front.
  • Zero-shot: no fine-tuning. Any numeric series with a regular step: demand, sales, sensor readings, traffic, prices.
  • Probabilistic: 21 quantiles for every step (0.01, 0.05, 0.1, ..., 0.95, 0.99), so you get a range as well as a number.
  • Long history, any horizon: up to 32,768 steps of history; the horizon is decoded in blocks of 1,024 steps.
  • More than one variable: several variates forecast jointly, plus past-only, known-future and categorical covariates.

The four sizes on Opper

ModelPrice per 1M input stepsPrice per 1M forecast stepsPrice of one forecast (2,016 steps to 96)Time per forecastLoad error, 28 days
Timestral 5MUSD 0.005USD 0.05USD 0.00001490.11 s2.45%
Timestral 28MUSD 0.01USD 0.10USD 0.00002980.21 s2.38%
Timestral 123MUSD 0.03USD 0.30USD 0.00008930.60 s2.37%
Timestral 209MUSD 0.05USD 0.50USD 0.0001490.79 s2.42%

The price of one forecast is the history read plus the steps forecast, here three weeks of 15-minute data (2,016 steps) to one day (96 steps). Time is one request with one series, measured end to end over the internet, median of three. Load error is the mean absolute percentage error from the test below.

Model pages, with the same prices and the data-handling facts: 5M, 28M, 123M, 209M. The catalog lists them under the forecast type:

curl -s "https://api.opper.ai/v3/models?type=forecast" \
  | jq -r '.models[] | "\(.id)\tin \(.pricing.input[0])\tout \(.pricing.output[0])"'

Your first forecast

model, prediction_length and series are required. Each series has a target, its history:

curl https://api.opper.ai/v3/forecast \
  -H "Authorization: Bearer $OPPER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "timestral-209m",
    "prediction_length": 4,
    "series": [{"target": [10, 12, 11, 13, 12, 14, 13, 15, 14, 16, 15, 17]}],
    "quantile_levels": [0.1, 0.5, 0.9]
  }'
{
  "model": "timestral-209m",
  "prediction_length": 4,
  "quantile_levels": [0.1, 0.5, 0.9],
  "forecasts": [{
    "median": [15.92, 17.98, 16.93, 19.07],
    "quantiles": {
      "0.1": [15.63, 17.61, 16.48, 18.48],
      "0.5": [15.92, 17.98, 16.93, 19.07],
      "0.9": [16.28, 18.29, 17.37, 19.58]
    }
  }],
  "usage": {"series": 1, "input_steps": 12, "forecast_steps": 4}
}
Twelve history points rising in a zigzag from 10 to 17, and Timestral 209M's four-step forecast continuing the zigzag upward to 19.07, with a narrow 80% interval

The model picked up both the trend and the up-down zigzag from twelve points. The cost is in the X-Opper-Cost header (here USD 0.0000026), and the call shows up in your traces like any other.

Leave out quantile_levels to get all 21. Two more options default to on, as in Mistral's code: symmetric_averaging (also forecast the negated series and average the two; twice the compute, same price) and clip_nonnegative (keep the forecast at or above zero when the history never goes below it). Turn the second off for a series that can go negative, such as electricity prices.

The same endpoint is on the gateway at https://gw.opper.ai/opper/v1/forecast and https://eu.gw.opper.ai/opper/v1/forecast, with the same body.

A real test: German electricity load

Grid load is the case Mistral's announcement uses: one day ahead, from three weeks of 15-minute history. We took Germany's measured load from Energy-Charts by Fraunhofer ISE and forecast each of the 28 days from 12 October to 8 November 2026 at 00:00 UTC, from the 2,016 steps before it. Every size got the same 28 histories, seven series to a request. The baseline is the usual naive forecast for load: the same 15 minutes one week earlier.

Actual German load for the week of 2 to 8 November 2026 against Timestral 209M's day-ahead median and 80% interval
ForecastMean absolute errorMean absolute % errorActuals inside the 80% interval
Same 15 minutes last week2.26 GW4.07%n/a
Timestral 5M1.38 GW2.45%84.0%
Timestral 28M1.33 GW2.38%82.6%
Timestral 123M1.33 GW2.37%80.6%
Timestral 209M1.35 GW2.42%78.8%
Mean absolute percentage error of the seasonal baseline and the four Timestral sizes on 28 days of German load

What the numbers say:

  • Every size cut the baseline's error by about 40%, from 4.07% to between 2.37% and 2.45%, on a series averaging 55 GW. 209M beat the baseline on 26 of the 28 days.
  • The sizes were within 0.08 percentage points of each other. 123M was best and 5M last, at a sixth of 123M's price and a fifth of its time. Mistral's benchmarks rank 209M first over many datasets; on one clean, strongly seasonal series like national load, the small models already find what there is to find.
  • The intervals are honest: 79% to 84% of actual values fell inside the 80% interval, and the 0.9 quantile was above the actual load 87% to 91% of the time. That is what makes a quantile usable for planning capacity.
  • The worst day was Monday 2 November (5.9% on 209M, and one of the baseline's worst days too), with the forecast low through the working day. Calendar effects such as public holidays and school breaks are not in a series' history unless you pass them as covariates.

Known covariates: day-ahead prices with wind and solar

Prices are harder. The German day-ahead price swings with wind and solar output, which the price's own history cannot see coming. That is what known covariates are for: rows you pass for the history and the forecast window both, such as a weather forecast, a promotion calendar or holidays.

Each covariate row is as long as the target plus prediction_length:

{
  "model": "timestral-209m",
  "prediction_length": 96,
  "clip_nonnegative": false,
  "series": [{
    "target": [/* 2,016 prices, EUR/MWh */],
    "known_covariates": [
      [/* wind, 2,016 + 96 values */],
      [/* solar, 2,016 + 96 values */],
      [/* load, 2,016 + 96 values */]
    ]
  }]
}

Same 28 days, the German-Luxembourg (DE-LU) day-ahead price as the target, from SMARD.de (Bundesnetzagentur, CC BY 4.0) through Energy-Charts:

ForecastMean absolute error (EUR/MWh)Actuals inside the 80% interval
Same 15 minutes yesterday40.7n/a
Same 15 minutes last week84.0n/a
Timestral 28M, price history only27.482.4%
Timestral 209M, price history only27.181.2%
Timestral 28M, with wind, solar and load19.480.2%
Timestral 209M, with wind, solar and load17.779.3%
Actual German day-ahead prices for 2 to 8 November 2026 against Timestral 209M's forecast with wind, solar and load as known covariates

Prices over the 28 days averaged EUR 201/MWh and ranged from zero to EUR 574/MWh, which is why there is no percentage column: a percentage error is meaningless around zero.

  • From its own history, Timestral cut the error of "same as yesterday" by a third (40.7 to 27.1 EUR/MWh).
  • Wind, solar and load cut it by another third (27.1 to 17.7 on 209M, 27.4 to 19.4 on 28M), and on 209M covariates won on 20 of the 28 days.
  • The bigger model used the covariates better: without them 28M and 209M were 0.3 EUR/MWh apart, with them 1.7.
  • Covariates make a request heavier: four rows are read instead of one, so input cost is four times higher, and a request of seven series took about 32 seconds on 209M instead of 5.

One caveat on that row: we passed the measured wind, solar and load for the forecast day, where in production you would pass their forecasts. A weather forecast is less exact than the measurement, so read the covariate result as the most covariates can add here.

Past covariates (past_covariates, as long as the target) are for series you only know up to now, and the categorical variants take category codes, such as a store id or a weekday, and are target-encoded per series.

Many series, several variates, missing values

  • Many series in one request: put up to 1,024 in series, and they are forecast together. Series of different lengths are padded to the longest.
  • Several variates jointly: make target a list of equally long lists, such as the load of three regions, and the forecast keeps the same shape, so the model can use how they move together.
  • Missing values: null anywhere in a target or a covariate row.

What it costs

Opper bills per time step, from the response's usage: every step read (each target variate and each covariate row) at the input price, every step forecast at the forecast price. Nothing else, no per-request fee.

  • One day of 15-minute load from three weeks of history (2,016 read, 96 forecast) on 209M: USD 0.000149. A million such forecasts: USD 148.80.
  • The same on 5M: USD 0.0000149, or USD 14.88 per million.
  • With three known covariates, a request reads four rows instead of one, so its input cost is four times higher.

The whole 28-day test above, every model and both targets, cost under 3 US cents (USD 0.029).

Where it runs and what is kept

Opper runs all four models itself, in AWS's Stockholm region (eu-north-1), so a series stays in the EU and passes through no model provider on the way. The forecasting service stores nothing and logs only the shape of each request (how many series and steps), never the numbers. Opper's own record of the call, which you see in your traces (if enabled), follows your organization's retention setting, and at zero nothing is kept. An organization rule that allows only EU inference or zero data retention lets these calls through.

Limits

  • Up to 32,768 steps of history per series are read; longer histories keep the most recent.
  • Up to 8,192 steps of horizon, 1,024 series, about 4 million numbers and 64 MiB per request.
  • Bigger requests take longer: in the test, a request of seven series with three covariate rows each took about 32 seconds on 209M. A 503 means every forecast slot is taken; retry shortly.
  • A forecast is only as good as the history: a regime change the history never showed, such as a new tariff or a plant going offline, is invisible to the model unless you pass it as a covariate.

Which size to pick

  • Start with 28M. It was within 0.01 percentage points of the best on load, costs a fifth of 209M and answers a single series in about 0.2 seconds.
  • Pick 5M for very many series or a tight latency budget: about 0.1 seconds and USD 14.88 per million day-ahead forecasts of the shape above, at 0.08 points behind on load.
  • Pick 209M when you pass covariates or your series are noisier than national load. It gained the most from covariates in the price test, and it is the size that ranks first in Mistral's benchmarks.
  • Test on your own history first. Hold back the last few days, forecast them from what came before, and compare. It is the same request, and the 28-day test above cost under 3 cents.

Get started

Keep reading

All articles