# Time Series Forecasting - AI Sales & Demand Forecast (`tidytools/time-series-forecast`) Actor

Forecast sales, demand, traffic or any time series from CSV, a Google Sheet or JSON with Mistral AI's Timestral model: median forecast, prediction intervals, future dates and an optional backtest. Runs inside the Actor. $0.02 per series; too-short series are free.

- **URL**: https://apify.com/tidytools/time-series-forecast.md
- **Developed by:** [Yukai Lin](https://apify.com/tidytools) (community)
- **Categories:** AI, Developer tools, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $16.00 / 1,000 series forecasts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What does Time Series Forecasting do?

**Forecast any time series in one step**: paste a CSV, upload a spreadsheet export, link a Google Sheet or send JSON, and get the **median forecast**, **prediction intervals** (any quantiles you choose) and the **future dates** for every series. Sales, demand, orders, website traffic, prices, sensor readings, KPIs: if it is a column of numbers over time, it can be forecast. **$0.02 per series, no start fee; series that are too short or unreadable are free.**

The forecasts come from **Timestral**, the time-series foundation model Mistral AI released in October 2026. It is a *zero-shot* model: it was trained on a large collection of series, so it forecasts your data directly, with no model fitting, parameter tuning or data-science setup. The model runs **on CPU inside this Actor**: your data is not sent to any outside API.

- 📈 **Median + intervals**: 10th/90th percentiles by default, or any quantiles between 0.01 and 0.99 (e.g. 0.025/0.975 for a 95% interval)
- 📅 **Dates continue your history**: daily, business-day, weekly, monthly (month starts or month ends), quarterly, yearly, hourly and minute data are detected from the dates
- 🧮 **Many series per run**: one column per series (`date,sales,visits`), long format (`store,date,sales`), JSON arrays or objects
- 🕳️ **Messy data is fine**: gaps are handled by the model, duplicate dates are combined, dates in most formats are read (`2026-10-22`, `10/22/2026`, `22.10.2026`, `Oct 2026`, `2026-Q4`, `2026-W43`, Unix time)
- 🔁 **Resample on the way in**: choose a coarser frequency to turn daily orders into weekly totals (sum, mean, last, min or max)
- 🧪 **Optional backtest**: the Actor hides the last part of your history, forecasts it, and reports the error next to a seasonal-naive baseline, so you can see how far to trust the forecast for *your* data
- 📤 **Spreadsheet-ready output**: one dataset row per series plus `FORECAST_CSV`, one line per series and future step

### How to use it

1. Paste your data into **Data** (a header row and a date column work best), or give a **file or link** (Google Sheets, GitHub, Dropbox, any CSV/JSON URL).
2. Set the **Forecast horizon** (how many future steps) or leave it empty for a sensible default per frequency.
3. Optionally pick **quantiles**, turn on the **backtest**, and run.

#### Input example

```json
{
    "data": "date,store_a,store_b\n2026-01-05,120,98\n2026-01-12,131,101\n...",
    "horizon": 13,
    "quantiles": [0.1, 0.9],
    "evaluate": true
}
```

Long format (one series per id) and JSON work too:

```json
{
    "series": [
        { "id": "sku-1", "values": [12, 15, 14, 18, 21, 19, 24, 26, 25, 29], "start": "2026-01-01", "freq": "M" }
    ],
    "horizon": 6
}
```

A series can also carry its own dates: `"timestamps": ["2026-01-05", "2026-01-12", …]` next to `"values"`, or `"values": [["2026-01-05", 7], ["2026-01-12", 9], …]`.

#### Output example (local test run, 22 October 2026: airline passengers, monthly, linked from GitHub)

```json
{
    "seriesId": "Passengers",
    "success": true,
    "charged": true,
    "model": "Timestral-123M",
    "frequency": "month",
    "horizon": 12,
    "lastTimestamp": "1960-12-01",
    "lastValue": 432,
    "forecastStart": "1961-01-01",
    "forecastEnd": "1961-12-01",
    "nextMedian": 446.494,
    "nextLow": 431.479,
    "nextHigh": 462.438,
    "forecast": [
        { "step": 1, "timestamp": "1961-01-01", "median": 446.494, "q10": 431.479, "q90": 462.438 },
        { "step": 2, "timestamp": "1961-02-01", "median": 417.213, "q10": 398.03, "q90": 436.536 },
        { "step": 3, "timestamp": "1961-03-01", "median": 469.775, "q10": 447.229, "q90": 491.618 }
    ],
    "backtest": {
        "holdoutSteps": 12,
        "holdoutStart": "1960-01-01",
        "maeModel": 9.0749,
        "maeSeasonalNaive": 47.8333,
        "maseModel": 0.298,
        "maseSeasonalNaive": 1.5709,
        "errorVsSeasonalNaivePct": -81,
        "beatsSeasonalNaive": true,
        "intervalLevel": "10-90",
        "intervalCoverage": 0.917
    },
    "runtimeSecs": 0.644
}
```

`FORECAST_CSV` in the key-value store has every point in long format, ready for a spreadsheet or BI tool:

```
series_id,step,timestamp,median,q5,q25,q75,q95
GASREGW,1,2026-10-26,4.21193,4.08957,4.17008,4.25656,4.33909
GASREGW,2,2026-11-02,4.1439,3.90394,4.05963,4.23639,4.39239
```

A series that cannot be forecast gets a free row, e.g. `{ "seriesId": "too-short", "success": false, "charged": false, "errorType": "invalid_input", "error": "too short: 3 value(s), at least 8 are needed (not charged)" }`. The key-value store also holds `SUMMARY` (counts, model, time per series, backtest overview, notes about the input).

### How accurate is it?

Tested on 9 real public series (rolling origin: the last 5 forecast windows of each series, the model sees only the history before each window). Lower **MASE** is better; 1.0 is the error of a naive forecast on the history. The baseline repeats the last season (last week, last year).

| Series (public source) | Frequency | Length | Horizon | Seasonal naive | Timestral 28M | Timestral 123M | 10–90% interval hit rate (123M) |
|---|---|---|---|---|---|---|---|
| Daily minimum temperature, Melbourne 1981–90 | day | 3,650 | 30 | 0.939 | 0.667 | **0.662** | 80% |
| Wikipedia page views (log), Prophet example | day | 2,905 | 30 | 1.347 | 1.065 | **1.033** | 79% |
| US regular gasoline price (FRED GASREGW) | week | 1,888 | 13 | 1.612 | **0.665** | 0.751 | 74% |
| US initial jobless claims (FRED ICSA) | week | 3,119 | 13 | 0.170 | **0.097** | 0.098 | 85% |
| Airline passengers 1949–60 | month | 144 | 12 | 1.334 | 0.554 | **0.494** | 80% |
| Car sales, Quebec 1960–68 | month | 108 | 12 | 1.043 | 0.988 | **0.952** | 82% |
| US retail sales, Prophet example | month | 293 | 12 | 1.066 | 0.342 | **0.336** | 92% |
| US real GDP (FRED GDPC1) | quarter | 318 | 8 | **2.935** | 4.096 | 5.779 | 78% |
| Temperature every 5 minutes, Yosemite | 5 min | 18,721 | 288 | 0.578 | 0.551 | **0.490** | 96% |

The model beats the baseline on 8 of 9 series. The exception is instructive: for GDP the model wins 4 of the 5 windows by a wide margin, but the window that starts right after the 2020 COVID drop extrapolates the crash (a sudden break at the very end of the history). Use the **backtest** option to check your own data before you rely on a forecast.

### How much does it cost?

| Event | Free / Bronze | Silver | Gold and above |
|---|---|---|---|
| Series forecast | **$0.02** | $0.018 | $0.016 |

**No start fee.** Charged: each series that is forecast (median, quantiles and, if on, the backtest). **Not charged:** series with fewer than 8 values, series that cannot be read, series where the model fails, series not processed because of your limits, and the sample data pre-filled in **Data** when you also give a file/link or JSON series (the untouched sample is then ignored). Every row says `charged: true/false`.

Both models cost the same; the 123M model needs 2 GB of memory (default), the 28M model runs in 1 GB. Runs are short: in local tests on one CPU thread a series took 0.3–2.6 s including the backtest, after about 5 s of model loading (50 series of one year of daily data: 21 s in total). On Apify, runs with one or two monthly series took 16–26 s in total, model loading included (October 2026).

#### How it compares

Checked October 2026 on the Apify Store: searches for "forecast", "time series forecasting", "sales forecast", "demand forecast" and similar phrases found no general-purpose forecasting Actor, only Google Trends scrapers. Outside Apify, hosted forecasting APIs exist (for example on RapidAPI); this Actor's difference is the model (Timestral topped the GIFT-Eval, TIME and fev-bench leaderboards when Mistral AI released it; the 123M version used here is within 0.002 MASE of the largest one), per-series pricing, free failures and spreadsheet-style input.

### Control your cost

At the start the run logs its plan, e.g. `Plan: 12 series forecast × $0.02 = at most $0.24`. It never charges more than your **maximum charge per run**: series that do not fit are not processed and are listed in `SUMMARY.notProcessed`. **Max series** (default 1,000) caps one run. Wide CSV files with many numeric columns are many series: set **Value columns** to forecast only the ones you need. A run restarted by Apify skips series it already charged.

### Use with AI agents (MCP)

Connect Apify's MCP server (https://mcp.apify.com?tools=tidytools/time-series-forecast) to Claude, Cursor or any MCP client, then ask e.g. "Forecast the next 6 months of this sales series with an 80% interval: …".

```json
{ "series": [{ "id": "sales", "values": [120, 131, 128, 140, 152, 149, 160, 171], "start": "2026-01-01", "freq": "M" }], "horizon": 6 }
```

Failed or too-short series are not charged and carry an `errorType`.

### Integrations

Forecast the output of another Actor: export its dataset as CSV and give the link in **Or a file or link** (e.g. daily review counts, prices from a product tracker, traffic numbers). Schedule the Actor on Apify to refresh a forecast every week, and read `FORECAST_CSV` from Google Sheets (`=IMPORTDATA(...)`) or a BI tool.

### Tips and limitations

- **History**: at least 8 values per series; two full seasons (e.g. 24 months, 14 days) give the model a seasonal pattern to follow. Up to the last 4,096 steps are used by default ("Max history", up to 32,768).
- **Horizon**: up to 1,024 steps. Forecasts further than one or two seasons ahead mostly carry the trend and seasonality forward.
- **Breaks at the end of the history** (a crash, a promotion spike, a data error in the last values) can be extrapolated; check the backtest and clean obvious errors first.
- **One series at a time**: each series is forecast from its own history. Covariates (prices, holidays) and joint forecasts of related series are not part of this version.
- **Times are used as written**: a time-zone suffix is ignored so that daily data stays on its own day; Unix timestamps are read as UTC.
- **Non-negative data stays non-negative**: if the history has no negative value, the forecast and its intervals are not below 0.

### Model and licence

Forecasts are produced by **Timestral** (Timestral-123M and Timestral-28M, pinned versions from Hugging Face) with Mistral AI's open-source inference code (Apache-2.0). The model weights are published by Mistral AI under a **Modified MIT licence** (attribution notice: "2026 - Mistral AI"). Its main extra condition: the licence may not be used by a company whose global consolidated monthly revenue exceeds US$20 million (such companies can ask Mistral AI for a commercial licence). The full licence text is at https://huggingface.co/mistralai/Timestral-123M/blob/main/LICENSE and is shipped next to the weights inside this Actor. This Actor is an independent tool, not a Mistral AI product.

### Responsible use

Your series are processed inside the Actor run and stay in your own Apify storage; nothing is sent to Mistral AI or any other service (a link you give is downloaded once). A forecast is a statistical estimate, not a guarantee: use the intervals, not only the median, when you plan stock, staff or budgets.

### FAQ

**Does the Actor collect usage data?** The Actor sends one anonymous usage counter per run (input type, item count range, outcome) to improve the tool; no data values, URLs or account data.

**Which model should I choose?** Timestral 123M (default) was the more accurate one on 6 of the 9 test series above; 28M is about 4× faster and needs only 1 GB, at the same price. If you start a run with less than about 2 GB of memory, the 28M model is used automatically and the SUMMARY says so.

**What does the backtest measure?** It hides the last *horizon* steps (at most a third of the history), forecasts them from the rest, and compares the error (MAE, MASE, sMAPE) with a seasonal-naive forecast of the same steps. `intervalCoverage` is the share of hidden values inside the 10–90% interval (about 0.8 is well calibrated). It costs nothing extra but roughly doubles the run time.

**My CSV has several numeric columns. What is forecast?** Every numeric column except the date and id-like columns (id, code, zip, lat/lon, index), each as one charged series. Set **Value columns** to choose. In long format (an id column such as store or sku and repeated dates), one column is forecast per id: the one named value/sales/y/amount…, otherwise the last numeric column.

**My data has gaps or several rows per day.** Gaps are left as missing (the model handles them) unless you choose zero or "repeat previous" under **Missing steps**. Several rows in the same step are combined with **Combine values** (sum by default; use mean for prices or temperatures).

**What if my input is empty or nothing can be forecast?** The run still ends normally: you get free rows with `errorType: "invalid_input"` explaining why (too short, no numeric column, unreadable dates), and `SUMMARY.status` is `NO_RESULTS`. Nothing is charged.

# Actor input Schema

## `data` (type: `string`):

Paste your history. CSV or tab-separated with a header: a date column plus one column per series ("date,sales,visits"), or long format "id,date,value" (one series per id). Also accepted: plain numbers ("12, 15, 14, 18…"), a JSON array of numbers, or JSON series. Each series needs at least 8 values; shorter ones get a free error row.

## `dataUrl` (type: `string`):

Upload a CSV/TSV/JSON file (up to 50 MB) or paste a link: a Google Sheets link (shared with "anyone with the link"), a GitHub file link, Dropbox, or any direct CSV/JSON URL. Excel files: save as CSV first.

## `horizon` (type: `integer`):

How many future steps to forecast (e.g. 12 months, 30 days, 13 weeks). Empty = by frequency: 12 months, 30 days, 13 weeks, 8 quarters, 5 years, 48 hours.

## `frequency` (type: `string`):

Step of the series. Choosing a coarser step than your data aggregates it (e.g. daily orders → weekly totals, see "Combine values"). Without dates, set it to get dates on the forecast (JSON series with "start").

## `quantiles` (type: `array`):

Quantiles to return besides the median, between 0.01 and 0.99 (0.1 and 0.9 = an 80% interval; 0.025 and 0.975 = 95%). Output keys are q10, q90, q2.5…

## `model` (type: `string`):

Mistral AI's Timestral zero-shot forecasting models, run on CPU inside this Actor. Same price for both.

## `evaluate` (type: `boolean`):

Also forecast the last part of the history as if it were unknown and compare the error with a seasonal-naive forecast (repeat last season). Adds a "backtest" object to each row; no extra charge, about twice the run time.

## `series` (type: `array`):

For API and AI-agent use: \[{"id": "sku-1", "values": \[12, 15, 14], "timestamps": \["2026-01-01", …]}] or with "start": "2026-01-01" and "freq": "D" | "W" | "M" | "Q" | "Y" | "H". Also \[\[date, value], …] pairs and plain arrays of numbers.

## `dateColumn` (type: `string`):

Header name or column number (1 = first) of the dates. Empty = detect (a column named date, month, ds, period… or the one that reads as dates).

## `valueColumns` (type: `array`):

Columns to forecast (header names or numbers). Empty = every numeric column except id-like ones (each is one charged series); in long format the column named value/sales/y…, else the last numeric one.

## `idColumn` (type: `string`):

Column that names the series in long data (e.g. store, sku, region). Empty = detect when dates repeat.

## `aggregation` (type: `string`):

When several rows fall into one step (duplicate dates, or a coarser Frequency than the data).

## `fillMissing` (type: `string`):

Steps between the first and last date that have no value.

## `maxSeries` (type: `integer`):

Cost cap: at most this many series are forecast in one run (the rest are listed in SUMMARY, not charged).

## `maxHistory` (type: `integer`):

Only the most recent steps of each series are used (more history rarely helps and takes longer). Timestral reads up to 32,768.

## Actor input object example

```json
{
  "data": "Month,Passengers\n1957-01,315\n1957-02,301\n1957-03,356\n1957-04,348\n1957-05,355\n1957-06,422\n1957-07,465\n1957-08,467\n1957-09,404\n1957-10,347\n1957-11,305\n1957-12,336\n1958-01,340\n1958-02,318\n1958-03,362\n1958-04,348\n1958-05,363\n1958-06,435\n1958-07,491\n1958-08,505\n1958-09,404\n1958-10,359\n1958-11,310\n1958-12,337\n1959-01,360\n1959-02,342\n1959-03,406\n1959-04,396\n1959-05,420\n1959-06,472\n1959-07,548\n1959-08,559\n1959-09,463\n1959-10,407\n1959-11,362\n1959-12,405\n1960-01,417\n1960-02,391\n1960-03,419\n1960-04,461\n1960-05,472\n1960-06,535\n1960-07,622\n1960-08,606\n1960-09,508\n1960-10,461\n1960-11,390\n1960-12,432",
  "horizon": 12,
  "frequency": "auto",
  "quantiles": [
    0.1,
    0.9
  ],
  "model": "timestral-123m",
  "evaluate": true,
  "aggregation": "sum",
  "fillMissing": "missing",
  "maxSeries": 1000,
  "maxHistory": 4096
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `forecastCsv` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `full` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "data": `Month,Passengers
1957-01,315
1957-02,301
1957-03,356
1957-04,348
1957-05,355
1957-06,422
1957-07,465
1957-08,467
1957-09,404
1957-10,347
1957-11,305
1957-12,336
1958-01,340
1958-02,318
1958-03,362
1958-04,348
1958-05,363
1958-06,435
1958-07,491
1958-08,505
1958-09,404
1958-10,359
1958-11,310
1958-12,337
1959-01,360
1959-02,342
1959-03,406
1959-04,396
1959-05,420
1959-06,472
1959-07,548
1959-08,559
1959-09,463
1959-10,407
1959-11,362
1959-12,405
1960-01,417
1960-02,391
1960-03,419
1960-04,461
1960-05,472
1960-06,535
1960-07,622
1960-08,606
1960-09,508
1960-10,461
1960-11,390
1960-12,432`,
    "horizon": 12,
    "quantiles": [
        0.1,
        0.9
    ],
    "evaluate": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidytools/time-series-forecast").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "data": """Month,Passengers
1957-01,315
1957-02,301
1957-03,356
1957-04,348
1957-05,355
1957-06,422
1957-07,465
1957-08,467
1957-09,404
1957-10,347
1957-11,305
1957-12,336
1958-01,340
1958-02,318
1958-03,362
1958-04,348
1958-05,363
1958-06,435
1958-07,491
1958-08,505
1958-09,404
1958-10,359
1958-11,310
1958-12,337
1959-01,360
1959-02,342
1959-03,406
1959-04,396
1959-05,420
1959-06,472
1959-07,548
1959-08,559
1959-09,463
1959-10,407
1959-11,362
1959-12,405
1960-01,417
1960-02,391
1960-03,419
1960-04,461
1960-05,472
1960-06,535
1960-07,622
1960-08,606
1960-09,508
1960-10,461
1960-11,390
1960-12,432""",
    "horizon": 12,
    "quantiles": [
        0.1,
        0.9,
    ],
    "evaluate": True,
}

# Run the Actor and wait for it to finish
run = client.actor("tidytools/time-series-forecast").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "data": "Month,Passengers\\n1957-01,315\\n1957-02,301\\n1957-03,356\\n1957-04,348\\n1957-05,355\\n1957-06,422\\n1957-07,465\\n1957-08,467\\n1957-09,404\\n1957-10,347\\n1957-11,305\\n1957-12,336\\n1958-01,340\\n1958-02,318\\n1958-03,362\\n1958-04,348\\n1958-05,363\\n1958-06,435\\n1958-07,491\\n1958-08,505\\n1958-09,404\\n1958-10,359\\n1958-11,310\\n1958-12,337\\n1959-01,360\\n1959-02,342\\n1959-03,406\\n1959-04,396\\n1959-05,420\\n1959-06,472\\n1959-07,548\\n1959-08,559\\n1959-09,463\\n1959-10,407\\n1959-11,362\\n1959-12,405\\n1960-01,417\\n1960-02,391\\n1960-03,419\\n1960-04,461\\n1960-05,472\\n1960-06,535\\n1960-07,622\\n1960-08,606\\n1960-09,508\\n1960-10,461\\n1960-11,390\\n1960-12,432",
  "horizon": 12,
  "quantiles": [
    0.1,
    0.9
  ],
  "evaluate": true
}' |
apify call tidytools/time-series-forecast --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tidytools/time-series-forecast"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/COSoOaCPkdPftrS9W/builds/RLE8HthtEpinqKn9J/openapi.json
