Patterns & Segmentation.
Common needs do not require a special endpoint: a forecast per customer segment, a revenue total split
into its parts, or a churn rate projection. This page shows how to build
each one with /v2/forecast and /v2/batch/forecast.
Forecast each segment separately
Use tenant_context to split one metric across a dimension. The pair
identifier + tenant_context identifies a series. So mrr on
plan-pro and mrr on plan-free are two independent series.
Each one has its own history and its own accuracy tracking.
Despite the name, it is not only for multi-tenancy. Use it for any dimension you want to forecast: plan, region, store, channel, cohort, acquisition month, device type.
Batch accepts up to 100,000 series per call on a paid plan (10 on the free tier), so a whole segmentation fits in one request:
curl -X POST "https://forecastapi.com/v2/batch/forecast" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"series": [
{
"identifier": "mrr",
"tenant_context": "plan-pro",
"data": [{"date": "2024-01-01", "value": 41200}, {"date": "2024-02-01", "value": 43900}]
},
{
"identifier": "mrr",
"tenant_context": "plan-free",
"data": [{"date": "2024-01-01", "value": 2100}, {"date": "2024-02-01", "value": 2450}]
},
{
"identifier": "mrr",
"tenant_context": "plan-enterprise",
"data": [{"date": "2024-01-01", "value": 88000}, {"date": "2024-02-01", "value": 91500}]
}
],
"frequency": "M",
"periods": 6,
"data_type": "revenue"
}'
Results return in results.data, keyed by entity ID rather than by your identifier.
The same identifier can appear under several segments in one batch, so it would not be unique. Each
result echoes its own identifier and tenant_context, so match on that
pair.
tenant_context values later, the API creates new series that start empty. It does not
relabel the series you already have.Break a total into components
Send each part of a total as its own series and add the parts on your side. The parts might be new, expansion, contraction and churned revenue, or a split by category, channel or region. You then get a forecast per component instead of one aggregate number.
{
"series": [
{"identifier": "revenue-new",
"data": [{"date": "2024-01-01", "value": 12000}, {"date": "2024-02-01", "value": 13100}]},
{"identifier": "revenue-expansion",
"data": [{"date": "2024-01-01", "value": 4300}, {"date": "2024-02-01", "value": 4650}]},
{"identifier": "revenue-contraction",
"data": [{"date": "2024-01-01", "value": -1800}, {"date": "2024-02-01", "value": -1720}]},
{"identifier": "revenue-churned",
"data": [{"date": "2024-01-01", "value": -3100}, {"date": "2024-02-01", "value": -2950}]}
],
"frequency": "M",
"periods": 6,
"data_type": "revenue"
}
If you also forecast the total as its own series, the components will not sum to it. The API fits each series independently, and each series picks its own model and trend, so the two numbers will disagree. When the roll-up itself is the deliverable, use grouped forecasting instead. It reconciles the whole hierarchy, so the parts sum to the whole by construction. If you stay on batch, publish one figure: either the sum of the parts or the standalone total, not both.
lower value does not give you a lower bound for the sum. It
assumes every component misses low at the same time, which is far more pessimistic than reality.
Sum the point forecasts instead and treat the total's uncertainty as an open question. Or let
grouped forecasting build the aggregate
band under a stated correlation assumption. For the equivalent problem across periods rather than
across series, accumulate handles it explicitly.Forecast rates and percentages
Churn rate, conversion rate, utilization and margin are all ordinary time series, and they forecast normally. Send them in whatever unit you already use, 0–1 or 0–100, as long as you stay consistent.
Tell the API the range your series cannot leave with value_bounds. Either side is
optional. A rate has both bounds, and a backlog has only a floor:
{
"identifier": "churn-rate",
"data": [{"date": "2024-01-01", "value": 2.4}, {"date": "2024-02-01", "value": 3.1}],
"frequency": "M",
"periods": 12,
"value_bounds": {"min": 0, "max": 100}
}
The API then forecasts the series on a transformed scale and maps it back, so the forecast, the confidence bounds, and every quantile stay inside the range. The band is also no longer symmetric. On a rate near zero it has more room above than below, which is the honest shape for a number that cannot go below zero.
value_bounds changes what the
API forecasts rather than what it displays, so the stated coverage still holds.model_info.bounds_transform in the response. It says
whether the transform ran, and why not if it did not. The usual cause is a unit mismatch. Rates of
0–1 with a declared {"min": 0, "max": 100} work, but the reverse does not.Build a fan chart
A fan chart needs several quantiles per period. Request them with the
quantiles parameter. Each forecast row then returns a quantiles
object alongside the usual bounds:
{
"identifier": "SKU-12345",
"data": [{"date": "2024-01-01", "value": 120}, {"date": "2024-02-01", "value": 135}],
"frequency": "M",
"periods": 6,
"quantiles": [0.1, 0.3, 0.5, 0.7, 0.9]
}
This is the intuitive approach, but it costs more and gives you less. The API does not cache results, so five confidence levels bill as five forecasts.
More importantly, 0.80 is the widest band the foundation models produce natively.
advanced-quantized, advanced-patched, multivariate and
ensemble return the same 80% band whether you ask for 0.85 or 0.95, so those
lines overlap.
One quantiles request gives you genuinely distinct levels, read from each model's
own quantile heads, for a single charge.
What is not directly supported
Per-customer churn scoring and per-customer LTV. The question "which of these customers will churn, and what is each one worth" means you classify customers by their attributes: plan, tenure, usage, support history. That is a different kind of model from time series forecasting. It needs data that this API does not accept.
The aggregate version does work well: forecast a churn rate or revenue series per cohort with
tenant_context, then use accumulate to turn that forecast into a
discounted lifetime total. That tells you what a segment is worth, rather than what one customer is
worth.