# Documentation
> Complete documentation for Large Language Models
---
## Document: /documentation
URL: /documentation
# Overview
Welcome to The Forecasting Company API. We expose our own, as well as open-source foundation models,
behind a unified REST API at `https://api.retrocast.com`.
## Quick start
```bash
curl -X POST "https://api.retrocast.com/forecast?model=tabpfn-ts" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"series": [{
"index": ["2024-01-01","2024-01-02","2024-01-03","2024-01-04","2024-01-05"],
"target": [125, 120, 140, 135, 133]
}],
"horizon": 5,
"freq": "D",
"quantiles": [0.1, 0.5, 0.9]
}'
```
Or with the **Python SDK**:
```bash
pip install theforecastingcompany
```
```python
from theforecastingcompany import TFCClient, TFCModels
client = TFCClient() # reads TFC_API_KEY from environment
forecast_df = client.forecast(
train_df,
model=TFCModels.TabPFN_TS,
horizon=12,
freq="W",
quantiles=[0.1, 0.5, 0.9],
)
```
## Authentication
All API requests require a Bearer token. Obtain your API key at
[https://docs.retrocast.com/settings/api-keys](https://docs.retrocast.com/settings/api-keys).
Include it in every request header:
```
Authorization: Bearer YOUR_API_KEY
```
## Available models
| Model | Description | Covariates? | Forecast? | Retrocast? |
| -------------- | ---------------------------------------- | ---------------------- | --------- | ---------- |
| `t0-alpha` | The Forecasting Company's model | ✅ | ✅ | ✅ |
| `tfc-global` | Global model for time series | ✅ | ✅ | ✅ |
| `tabpfn-ts` | Foundation model from Uni. of Freiburg | ✅ | ✅ | ✅ |
| `moirai-2` | Salesforce's Moirai v.2 | ✅ | ✅ | ✅ |
| `timesfm-2` | Google's TimesFM 2.0 | Future covariates only | ✅ | ✅ |
| `timesfm-2p5` | Google's TimesFM 2.5 | Future covariates only | ✅ | ✅ |
| `chronos-2` | Amazon's Chronos-2 | ✅ | ✅ | ✅ |
| `tirex` | NXAI's model based on xLSTMs | ❌ | ✅ | ✅ |
| `aarima` | Auto-ARIMA | Soon | ✅ | ✅ |
| `aets` | Auto-ETS | Soon | ✅ | ✅ |
| `toto-2` | Datadog's Toto v.2 | ❌ | ✅ | ✅ |
### Models that support exogenous variables (covariates)
Only `tabpfn-ts`, `moirai-2`, and `chronos-2` support full exogenous variables.
`timesfm-2` and `timesfm-2p5` support future covariates only.
`tfc-global` supports all covariate types including string covariates and static variables.
### Models that support string covariates
Only `tabpfn-ts` and `tfc-global` support non-numeric (string) covariates. All other models require numeric covariates only.
## Supported frequencies
The `freq` field must be one of:
| Value | Meaning |
| ------- | ------------- |
| `5min` | 5 minutes |
| `15min` | 15 minutes |
| `30min` | 30 minutes |
| `H` | Hourly |
| `D` | Daily |
| `W` | Weekly |
| `M` | Monthly |
| `Q` | Quarterly |
| `Y` | Yearly |
---
# Endpoints
## POST `/forecast`
Perform inference with foundation models. Returns forecasts directly for each request.
### Query parameters
| Parameter | Type | Required | Default | Description |
| --------- | ------ | -------- | ------------ | ----------- |
| `model` | string | Yes | `t0-alpha` | The forecasting model identifier (see Available Models above) |
| `cloud` | string | No | any | Cloud provider for inference: `gcp`, `aws`, or `oci` |
### Request body
| Field | Type | Required | Default | Description |
| -------------- | ----------------- | -------- | ------------------- | ----------- |
| `series` | `InputSerie[]` | Yes | | Array of time series to forecast |
| `horizon` | integer (1–10000) | Yes | | Number of steps to forecast |
| `freq` | string | Yes | | Frequency of the time series (see Supported Frequencies) |
| `context` | integer or null | No | null (auto) | Number of history steps to use. Auto-set to the smaller of the model max or the length of target |
| `quantiles` | number[] or null | No | `[0.1, 0.9, 0.4, 0.5]` | Quantiles to return in the prediction |
| `covariates` | `Covariate[]` or null | No | null | Additional covariates provided by TFC (holidays, events, etc.) |
| `model_config` | object or null | No | null | Model-specific configuration overrides |
### InputSerie schema
Each series object has the following fields:
| Field | Type | Required | Default | Description |
| ----------------------- | ----------------- | -------- | ------- | ----------- |
| `target` | number[] | Yes | | Numeric target values (min 1 item) |
| `index` | string[] | Yes | | Dates (YYYY-MM-DD), datetimes, or timestamps matching target length |
| `hist_variables` | object | No | `{}` | Dict of variable name → historical numeric values. Length must match target |
| `future_variables` | object | No | `{}` | Dict of variable name → future numeric values. Length = target length + horizon |
| `future_variables_index`| string[] | No | `[]` | Dates/timestamps for the future variables |
| `static_variables` | object | No | `{}` | Dict of variable name → single numeric value (e.g., population, SKU ID) |
| `fcds` | integer[] or null | No | null | Forecast creation dates as target indexes. Used for backtesting. If null, uses the latest date |
| `only_as_context` | boolean | No | false | If true, series is used only as context (not forecasted). Only for global models |
### Example: Simple univariate forecast
```json
{
"series": [
{
"index": ["2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04", "2024-01-05"],
"target": [125, 120, 140, 135, 133]
}
],
"horizon": 5,
"freq": "D",
"quantiles": [0.1, 0.5, 0.9]
}
```
### Example: Multiple series
```json
{
"series": [
{
"index": ["2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04", "2024-01-05"],
"target": [125, 120, 140, 135, 133]
},
{
"index": ["2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04", "2024-01-05"],
"target": [500, 300, 200, 100, 200]
}
],
"horizon": 5,
"freq": "D",
"quantiles": [0.1, 0.5, 0.9]
}
```
### Example: Forecast with covariates (exogenous variables)
```json
{
"series": [
{
"index": ["2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04", "2024-01-05"],
"target": [125, 120, 140, 135, 133],
"hist_variables": {
"temperature": [74, 72, 79, 77, 75]
},
"future_variables": {
"local_attendance_forecast": [125, 75, 200, 122, 123, 150, 100, 120, 121, 119]
},
"future_variables_index": [
"2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04", "2024-01-05",
"2024-01-06", "2024-01-07", "2024-01-08", "2024-01-09", "2024-01-10"
],
"static_variables": {
"Population": 100000
}
}
],
"horizon": 5,
"freq": "D",
"quantiles": [0.1, 0.5, 0.9],
"covariates": [
{
"type": "holidays",
"config": { "country": "US" }
}
]
}
```
### Example: With model config (Auto-ARIMA)
```json
{
"series": [
{
"index": ["2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04", "2024-01-05"],
"target": [125, 120, 140, 135, 133]
}
],
"horizon": 5,
"freq": "D",
"quantiles": [0.1, 0.5, 0.9],
"model_config": {
"model": "aarima",
"config": { "season_length": 7 }
}
}
```
### Response
```json
{
"status": "completed",
"series": [
[
{
"prediction": {
"mean": [138.2, 141.5, 139.8, 142.1, 140.3],
"0.1": [130.1, 132.4, 131.2, 133.5, 131.8],
"0.5": [138.0, 141.3, 139.6, 141.9, 140.1],
"0.9": [146.3, 150.6, 148.4, 150.7, 148.8]
},
"index": ["2024-01-06", "2024-01-07", "2024-01-08", "2024-01-09", "2024-01-10"],
"metrics": null
}
]
]
}
```
The `series` field is a nested array: `series[i][j]` is the prediction for the i-th input series at the j-th forecast creation date. Each prediction contains:
- `prediction`: dict of quantile → values. Always includes `mean`. Other keys match the requested quantiles.
- `index`: the forecast dates
- `metrics`: accuracy metrics (when the target is known, e.g., during backtesting): `mae`, `mape`, `crps`, `wape`, `scaled_bias`
---
## POST `/forecast-jobs`
Submit a long-running forecast job (e.g., backtesting over many forecast creation dates, or slow statistical models). Returns a `call_id` to poll for results.
### Query parameters
| Parameter | Type | Required | Default | Description |
| --------- | ------ | -------- | ----------- | ----------- |
| `model` | string | Yes | `t0-alpha` | The forecasting model identifier |
### Request body
Same as `/forecast` (see above). The key difference is that `fcds` (forecast creation dates) can be used to request multiple historic forecasts for the same series, useful for backtesting.
### Response
```json
{
"call_id": "fc-abc123def456"
}
```
Use this `call_id` with the GET endpoint below to retrieve results.
---
## GET `/forecast-jobs/{job_id}`
Retrieve the result of a previously submitted forecast job.
### Path parameters
| Parameter | Type | Required | Description |
| --------- | ------ | -------- | ----------- |
| `job_id` | string | Yes | The job ID returned by POST `/forecast-jobs` |
### Response (job completed)
Same format as the `/forecast` response with `"status": "completed"`.
### Response (job still running)
```json
{
"status": "in_progress",
"series": null
}
```
### Error responses
- `403 Forbidden` — you don't have access to this job
- `404 Not Found` — the job ID does not exist
---
## GET `/warmup`
Warmup a model endpoint for better UX in embedded applications. Cold models may take a few seconds on the first inference call; warming up pre-loads them.
### Query parameters
| Parameter | Type | Required | Default | Description |
| --------- | ------ | -------- | ----------- | ----------- |
| `model` | string | Yes | `t0-alpha` | The forecasting model to warmup |
| `cloud` | string | No | any | Cloud provider: `gcp`, `aws`, or `oci` |
---
## GET `/openapi`
Returns the OpenAPI 3.1 specification for this API in JSON format. No authentication required.
---
# Covariates
Covariates are additional variables that can improve forecast accuracy. They come in two forms:
## User-provided covariates
Pass these directly in the `series` objects:
- **`hist_variables`** — variables with values only for the historical period (same length as `target`). Example: recorded temperature.
- **`future_variables`** — variables with values for both history and forecast period. Length = history + horizon. Example: planned promotions, weather forecasts.
- **`static_variables`** — variables that don't change over time. Single numeric value per variable. Example: population, store size.
## TFC-provided covariates
Use the `covariates` array in the request body to automatically add covariates managed by TFC:
### Holidays
```json
{
"type": "holidays",
"config": {
"country": "US",
"smoothing_method": "auto",
"window_size": 15,
"sigma": 7.0,
"ramp_days": 14
}
}
```
- `country` (required): 2-character ISO country code (e.g., `US`, `GB`, `FR`, `DE`)
- `smoothing_method`: `auto` (default), `none`, `gaussian`, `exponential`, `ramp`, `flat`
- `window_size`: 1–15 (default 15), should be odd
- `sigma`: standard deviation for Gaussian kernel (default 7.0)
- `ramp_days`: days before/after for ramp (1–35, default 14)
### Events
```json
{
"type": "events",
"config": {
"country": "US"
}
}
```
- `country` (required): ISO country code or `"Global"`
### Temporal features
```json
{
"type": "temporal_features",
"config": {}
}
```
Automatically adds time-based features (day of week, month, etc.).
---
# Model-specific configuration
Some models accept additional configuration via `model_config`:
### Auto-ARIMA (`aarima`)
```json
{
"model_config": {
"model": "aarima",
"config": { "season_length": 7 }
}
}
```
- `season_length` (integer or null): Override automatic seasonal period detection.
### Auto-ETS (`aets`)
```json
{
"model_config": {
"model": "aets",
"config": { "season_length": 12 }
}
}
```
- `season_length` (integer or null): Override automatic seasonal period detection.
### Chronos-2 (`chronos-2`)
```json
{
"model_config": {
"model": "chronos-2",
"config": { "is_global": true }
}
}
```
- `is_global` (boolean, default false): Enable influence across the time-series — useful if all passed-in time-series are related (e.g., same product, different stores).
---
# Python SDK
For a higher-level interface, use the official Python SDK: [`theforecastingcompany`](https://pypi.org/project/theforecastingcompany/).
```bash
pip install theforecastingcompany
```
```python
from theforecastingcompany import TFCClient, TFCModels
client = TFCClient() # reads TFC_API_KEY env var
```
### Simple forecast
```python
import pandas as pd
from theforecastingcompany import TFCClient, TFCModels
client = TFCClient()
train_df = pd.DataFrame({
"unique_id": ["store_1"] * 100,
"ds": pd.date_range("2024-01-01", periods=100, freq="D"),
"target": range(100),
})
forecast_df = client.forecast(
train_df,
model=TFCModels.TabPFN_TS,
horizon=14,
freq="D",
quantiles=[0.1, 0.5, 0.9],
)
```
### Forecast with exogenous variables
```python
forecast_df = client.forecast(
train_df,
future_df=future_df,
model=TFCModels.TFCGlobal,
horizon=12,
freq="W",
static_variables=["region", "store_type"],
future_variables=["price", "promotion"],
add_holidays=True,
country_isocode="US",
)
```
### Cross-validation (backtesting)
```python
cv_df = client.cross_validate(
train_df,
model=TFCModels.TabPFN_TS,
horizon=12,
freq="W",
fcds=[pd.Timestamp("2024-06-01"), pd.Timestamp("2024-07-01"), pd.Timestamp("2024-08-01")],
quantiles=[0.1, 0.5, 0.9],
)
```
### Custom column names
```python
forecast_df = client.forecast(
my_data,
model=TFCModels.TimesFM_2,
horizon=7,
freq="D",
id_col="item_id",
date_col="date",
target_col="sales",
)
```
### Available TFCModels enum values
- `TFCModels.TFCGlobal` → `"tfc-global"`
- `TFCModels.TabPFN_TS` → `"tabpfn-ts"`
- `TFCModels.Moirai_2` → `"moirai-2"`
- `TFCModels.TimesFM_2` → `"timesfm-2"`
- `TFCModels.TimesFM_2p5` → `"timesfm-2p5"`
- `TFCModels.Chronos_2` → `"chronos-2"`
- `TFCModels.Tirex` → `"tirex"`
- `TFCModels.AutoARIMA` → `"aarima"`
- `TFCModels.AutoETS` → `"aets"`
### Data structure requirements
DataFrames must have:
- **`unique_id`** (or custom `id_col`): unique identifier for each time series
- **`ds`** (or custom `date_col`): datetime column
- **`target`** (or custom `target_col`): numeric values to forecast
### Batch size
Models like `chronos-2` and `moirai-2` support batching. Control with `batch_size` (default 256). Increase for speed, decrease if you encounter timeouts.
---
# Error handling
| HTTP Status | Description |
| ----------- | ----------- |
| `200` | Success |
| `401` | Unauthorized — invalid or missing API key |
| `403` | Forbidden — no access to this resource |
| `404` | Not found — resource does not exist |
| `422` | Unprocessable entity — invalid request body or parameters |
| `429` | Rate limited — too many requests |
| `500` | Internal server error |
## Support and feedback
Please reach out at `support@theforecastingcompany.com`
---
## Document: /api-v1/contents/intro
URL: /api-v1/contents/intro
# Describe your data, don't reshape it
Most forecasting APIs ask you to flatten your world. You take whatever lives in
your warehouse — stores, SKUs, regions, daily and weekly grains, gaps and all —
collapse it into a positional array, throw away the timestamps, and re-upload
the whole thing on every call:
```json
{
"values": [142, 137, 145, 139, ..., 90, 84, 88],
"series_lengths": [730, 365],
"horizon": 28
}
```
Every time-series is concatenated into that one array; `series_lengths` is all
that marks where one ends and the next begins. Your data has structure — and
it's the first thing this shape throws away, leaving you to rebuild it by hand,
in glue code, before every request.
Our beta API inverts that. **You describe your data; we do the reshaping.** You
hand us a schema that says what your columns *mean*, and a selector that says
which slice you want to forecast. Everything else — aggregating to the right
grain, filling gaps, rolling up a hierarchy, running a backtest across dozens of
dates — falls out of that one description, automatically handled by our
time-series engine.
## Describe a family once
A real dataset isn't one time-series. It's a *family* of them — every store,
every SKU, recorded at whatever grain the source happens to use. Here's the kind
of raw, long-format table that lands in your lake:
You don't reshape it — you describe it. Assign each column a role, say how each
signal behaves when it's combined, and declare how your identifiers roll up:
```json
{
"input": {
"source": "s3://acme/retail/sales/*.parquet",
"schema": {
"date": { "kind": "time", "frequency": "1d" },
"sku": "identifier",
"color": "identifier",
"product_family": "identifier",
"store": "identifier",
"city": "identifier",
"sales": { "kind": "target", "aggregate": "sum", "impute": { "const": 0.0 } }
},
"hierarchies": {
"sku": [["sku", "color"], ["sku", "product_family"]],
"store": [["store", "city"]]
}
},
"selector": { "product_family": "any", "city": "any" },
"prediction_length": "3w"
}
```
Read the `hierarchies` block as roll-up paths. These paths tell the API which
identifiers can be aggregated together. Individual SKUs can be grouped by color
or by product_family; individual stores can be grouped by city. The `selector`
is the dial: it picks which level you want to forecast. Click around and watch the
request change:
Roll SKUs up to families, stores into cities, days into weeks or months — and
you never hand-roll that `GROUP BY` again. Missing days, mixed grains, ragged
history: you declared how to handle them once, in the schema above.
## Point at the data where it lives
Your data already lives somewhere. There's no reason to serialize megabytes into
a request body to forecast it. Point the API at the source:
```json
"source": "s3://acme/retail/sales/*.parquet"
```
Inline arrays, parquet on object storage, a glob across many files — the same
schema describes all of them. Your lake stays the source of truth, and more
connectors (warehouses and databases) are on the way.
## One query, every forecast — and every backtest
Because you described a *family*, a single query fans out into a forecast for
every time-series it contains — every family, every city, in one request.
Once the API understands your data's shape, backtesting is another parameter,
not another pipeline. A backtest is just that forecast re-run from many points
in time, so say so:
```json
{
"input": {
"source": "s3://acme/retail/sales/*.parquet",
"schema": {
"date": { "kind": "time", "frequency": "1d" },
// rest of the schema...
}
},
"selector": { "product_family": "any", "city": "any" },
"context": "90d",
"prediction_length": "28d",
"cutoff": [{ "every": "2w" }],
"quantiles": [0.1, 0.5, 0.9]
}
```
`"cutoff": [{ "every": "2w" }]` re-runs the forecast from a cutoff every two
weeks, across every time-series, with 90 days of context each time. You get back
quantiles *and* accuracy metrics per cutoff — a full rolling-origin evaluation
from the request you were already going to send. Pass a list of explicit dates
instead, and you control exactly when each forecast is made.
Each cutoff reuses the same request shape: historical cutoffs become backtests,
and future-facing cutoffs become forecasts. Expand the controls to see how
`cutoff`, `lead_time`, `span`, and `prediction_length` carve up the timeline:
## Forecast among peers
Much of the signal for forecasting one time-series comes from its neighbors. A
newly launched product has no history of its own — but its predecessors do. So
instead of forecasting every time-series in isolation, you can pair each target
with the peers that should inform it — a small map from one selector to another.
Forecasting `{"sku": ["phone10"]}{:json}`? Let it borrow from `{"sku": ["phone8", "phone9"]}{:json}`.
A fresh store leans on the established ones nearby; this season's launch on the
last two generations. Each target points at its own hand-picked peers, and the
model sees them alongside the target at request time — no fine-tuning, no
separate training job. The forecast simply borrows the patterns it needs.
## Describe once, forecast anything
That's the whole idea. Describe your data where it lives, once — its grains, its
hierarchy, how its signals combine — then reuse that same description on every
request, changing only what you ask for: a slice, a different granularity, a
longer horizon, a year of backtests, a forecast leaning on its neighbors. The
shape of the request barely changes; the leverage is enormous.
This API is in beta. Grab an [API key](/settings/api-keys), point it at the data
you already have, describe it once, and forecast any slice of it you can name.
Questions or feedback? Reach us at support@theforecastingcompany.com.