> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hollerith.monarcha.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Forecasting

> Predict future values — in beta

Forecasting is Beta. It is implemented end to end and served.

It has not been validated to the bar we hold `fit` and `predict` to. The interface may still
change, and `evaluate()` does not cover it.

```python theme={null}
from hollerith import Hollerith

h = Hollerith()
result = h.forecast(sales, timestamp="week", target="units", prediction_length=12)

result.head(2)
#             mean   0.1   0.5   0.9
# timestamp
# 2026-07-06  41.2  33.0  41.0  50.1
# 2026-07-13  43.8  34.9  43.5  53.6
```

There is no `fit` step. The history you pass is the context the model reads while it
forecasts.

## One series

`context` is the history. Name the time column with `timestamp=` and the value to forecast
with `target=`; both default to columns of those literal names.

Give exactly one of `prediction_length` or `future`. Both or neither raises a `ValueError`,
and so does a `prediction_length` of zero or less.

## Quantile columns are strings

```python theme={null}
result["0.5"]     # the median forecast
result[0.5]       # KeyError
```

The frame is indexed by time, with a `mean` column and one column per quantile level. The
level is stringified on the wire, so `"0.9"` is a label and `0.9` is a `KeyError`.

Those interval columns are what turn a forecast into a decision: order to the `0.5`, hold
safety stock to the `0.9`. Levels default to 0.1, 0.5 and 0.9, and `quantiles=[...]` takes
any levels strictly between 0 and 1.

## Many series in one call

```python theme={null}
result = h.forecast(
    sales, timestamp="week", target="units", item_id="sku", prediction_length=12
)
result.loc["SKU-1042"]    # one SKU's forecast
```

Pass `item_id=` and every series is forecast in one call, against a MultiIndex of
`(item_id, timestamp)`. Short histories work, because the model reads every series you sent
while forecasting each one.

## Known future covariates

Any column in `context` that is not the timestamp, the target or the item id is a covariate.
When you know those values over the horizon, pass a `future` frame instead of
`prediction_length`.

```python theme={null}
future = pd.DataFrame({"week": ["2026-07-06", "2026-07-13"], "promo": [1, 0]})
result = h.forecast(sales, timestamp="week", target="units", future=future)
```

The covariate set is matched exactly, in two places:

* **Client-side.** A covariate missing from `future` raises `ValueError` naming the columns,
  before anything is uploaded.
* **Server-side.** The worker re-derives the covariate keys from your uploaded rows and
  compares them to the declared list. Any difference fails the job with `schema_mismatch`.

## Every call sends the whole history

Forecasting never uses a fitted context, so none of the fit-once, predict-many story in
[The fitted context](/concepts/fitted-context) applies here.

Every call re-uploads the full history and the model re-reads it. A forecast bills your
context rows once and your output rows twice, so a long history costs you on every call.

* **Trim the history to the window that carries signal.** Rows the model gains nothing from
  still cost upload time, forward-pass time and money.
* **Batch series rather than looping.** One call over 200 SKUs uploads one payload; 200 calls
  upload 200.

## Missing values are passed through

Nothing in the forecast path fills, drops, interpolates or resamples. A missing target or
covariate value is serialized as it stands and handed to the model.

Gaps in the timestamps are not filled either, and irregular spacing is not detected. Resample
to a regular grid and decide what a gap means before you call.

## Limits

| Limit                                  | Value       |
| -------------------------------------- | ----------- |
| Context rows                           | 1,000,000   |
| Output rows                            | 200,000     |
| Covariate columns                      | 2,000       |
| Context cells, rows × (covariates + 1) | 100,000,000 |

Output rows are the horizon times the number of series, or the row count of `future`. A
breach raises `ValidationError` with code `dataset_too_large` client-side, before the payload
is built.

## When this will not help

* **You need a measured accuracy number.** `evaluate()` works off a fitted training table, so
  it never covers a forecast. There is no accuracy path in the SDK for this call.
* **You forecast on a schedule.** Every call re-sends the whole history and the model re-reads
  it, so there is no fit-once amortisation to find.
* **Your covariates are themselves estimates.** A `future` frame you had to guess puts a
  forecast inside your forecast, and nothing separates the two errors.
* **The series is irregular or gappy.** Uneven spacing and missing periods are not detected.
  Resample to a regular grid first.

## Next

* [How Hollerith works](/concepts/how-hollerith-works) — why the table is the context
* [Limits](/reference/limits) — the full table, and the daily quota
* [Errors](/reference/errors) — `schema_mismatch` and `dataset_too_large` in full
* [Usage and billing](/account/usage-and-billing) — what a forecast bills
