# 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.