API REFERENCE

Endpoints.

This page is the complete reference for all ForecastAPI endpoints, parameters, and response formats.

Base URL
https://forecastapi.com/v2
Authentication
Bearer YOUR_API_KEY

Endpoints

Generate Forecast POST /v2/forecast

This endpoint generates forecasts for your time series data and selects the model automatically.

Request Body

REQUEST POST /v2/forecast
{
  "identifier": "SKU-12345",
  "data": [
    {"date": "2024-01-01", "value": 120},
    {"date": "2024-02-01", "value": 135},
    {"date": "2024-03-01", "value": 155}
  ],
  "periods": 6,
  "frequency": "M",
  "data_type": "sales",
  "confidence_level": 0.80
}

Parameters

identifier STRING · REQUIRED
Unique identifier for the data series (e.g., SKU, product ID)
data ARRAY · REQUIRED
Array of time series datapoints with date and value
periods INTEGER · REQUIRED
Number of forecast periods to generate (1-100 for hourly frequency)
frequency STRING · REQUIRED
Data frequency: D, W, M, Q, Y, H
data_type STRING · DEFAULT sales
Data type for optimized model selection. Default: "sales"
tenant_context STRING · OPTIONAL
A secondary key beside identifier. The same identifier under two different values makes two independent series. See Patterns & Segmentation.
confidence_level FLOAT · DEFAULT 0.80
Confidence level for intervals (0.10-0.99). Default: 0.80. Above 0.80 only standard widens further. See the note below.
quantiles ARRAY · OPTIONAL
Deciles to return per period, e.g. [0.1, 0.5, 0.9]. This adds a quantiles object to each forecast row.
value_bounds OBJECT · OPTIONAL
Range the series cannot leave, e.g. {"min": 0, "max": 100}. Either side is optional. The API forecasts the series on a transformed scale, so the forecast, both bounds, and every quantile stay inside the range. See forecasting rates and percentages.
adjustments ARRAY · OPTIONAL
A what-if scenario applied on top of the forecast. The response returns an adjusted block beside result.
accumulate OBJECT · OPTIONAL
This sums the forecast path into a single total, with optional decay and discounting. The response returns an accumulated block.
covariates ARRAY · OPTIONAL
Series that influence your data without being forecast themselves, such as a promotion calendar or a holiday flag. Only model: multivariate accepts these. Every other model rejects the request instead of forecasting without them. See Covariates.
current_period BOOLEAN · DEFAULT false
This projects how the current, partially observed period finishes. It adds your actuals to date to the forecast for the rest of the period. The response returns a current_period block beside result. See Current-Period Completion.
selection_metric STRING · DEFAULT auto
The back-testing error metric that picks the winning model. The options are auto (default), combined (0.6·MASE + 0.4·sMAPE), mase, and smape. auto uses combined for demand, sales, and inventory, and sMAPE for other data types.

quantiles, value_bounds, adjustments, and accumulate also work on POST /v2/batch/forecast. You set each one once for the whole request, or per series. See Batch Processing for how they scope and where the results land. current_period and covariates are the exceptions. They work on POST /v2/forecast only, and batch and grouped requests reject them instead of ignoring them silently.

Data Types

Specialized Types

These types support intermittent demand patterns:

  • "sales" - Sales data
  • "demand" - Demand forecasting
  • "inventory" - Inventory levels
Generic Types

These types use standard forecasting methods:

  • "web_traffic" - Website analytics
  • "cpu_usage" - System metrics
  • "revenue" - Financial data

Choosing the winning model (selection_metric)

For every request, the API back-tests a set of candidate models on your own history. It keeps the winning model, which is the one with the smallest error. selection_metric controls which error metric decides the winning model. A lower value is always better.

auto (default)
This uses combined for sales, demand, and inventory, and sMAPE for every other data type. It keeps the historical default behavior.
combined
A blend of 0.6·MASE + 0.4·sMAPE. It balances raw accuracy with trend capture, so sparse or intermittent series do not collapse onto a flat line.
mase
MASE measures error against a naive forecast. It is scale-free and reliable for intermittent demand.
smape
sMAPE is a symmetric percentage error. It is bounded from 0 to 200% and safe around zero values.
Caveats
  • This applies to the standard model. The advanced models (advanced-quantized, advanced-patched) honor mase and smape, but combined and auto use their own default metric. On single-model foundation forecasts, the API records the value but does not change the output.
  • Model comparison runs only when there is enough history to back-test. This needs at least 6 data points and periods greater than 1. With less data, no comparison runs and the field has no effect.
  • A change to the metric changes which model wins. This changes the forecast values themselves, not just how the API scores them. The winning model under smape is often not the winning model under combined.
  • The API does not offer MAPE. MAPE is undefined at zero and unstable near it, so the API ranks on sMAPE and MASE instead.

Response

RESPONSE 200 OK
{
  "result": {
    "identifier": "SKU-12345",
    "tenant_context": null,
    "forecasts": [
      { "period": 1, "date": "2024-04-01", "forecast": 168.5, "lower": 162.3, "upper": 174.7 },
      { "period": 2, "date": "2024-05-01", "forecast": 175.2, "lower": 168.1, "upper": 182.3 }
    ],
    "model_info": {
      "best_model": "AutoETS",
      "models_evaluated": ["AutoETS", "AutoARIMA", "AutoTheta", "SeasonalNaive"],
      "selection_metric": "smape",
      "interval_source": "conformal",
      "validation_performed": true,
      "smape": { "AutoETS": 6.1, "AutoARIMA": 7.4 },
      "mase": { "AutoETS": 0.82, "AutoARIMA": 0.95 }
    }
  },
  "meta": {
    "selection_metric": "smape",
    "timing": {
      "validation": 8.2,
      "selection": 45.6,
      "forecasting": 72.1,
      "total": 125.9
    }
  }
}

Response Fields

result OBJECT
The forecast payload. See the fields below.
result.identifier STRING
This echoes the series identifier from your request.
result.forecasts ARRAY
This holds one object per forecast period. Each object has period, date, forecast (the point value), and lower/upper prediction bounds.
result.model_info OBJECT
This holds the winning model (best_model), the models evaluated, the interval source, and the per-model back-testing scores (smape/mape/mase) when validation runs.
adjusted OBJECT
This appears only when you send adjustments. It is a sibling of result, not a field inside it. It carries the scenario path and stored: false.
accumulated OBJECT
This appears only when you send accumulate. It is also a sibling of result. It carries total, by_period, and the stated assumptions.
current_period OBJECT
This appears only when you send current_period: true. It is also a sibling of result. It projects how the current period finishes, from your actuals plus the forecast remainder.
meta.selection_metric STRING
The back-testing metric that chose the winning model
meta.timing OBJECT
Per-stage timings in milliseconds (validation, selection, forecasting, total)

Quantile Fan (quantiles)

quantiles asks for specific points of the predictive distribution per period, instead of just the two band edges. Use it to draw a fan chart, or to pick a service level directly (order to the 0.9 quantile rather than to the mean).

REQUEST POST /v2/forecast
{
  "identifier": "SKU-12345",
  "data": [{"date": "2024-01-01", "value": 120}, {"date": "2024-02-01", "value": 135}],
  "frequency": "M",
  "periods": 3,
  "quantiles": [0.1, 0.5, 0.9]
}

Each forecast row gains a quantiles object. The API does not change the existing lower and upper fields:

{
  "period": 1,
  "date": "2024-03-01",
  "forecast": 168.5,
  "lower": 162.3,
  "upper": 174.7,
  "quantiles": { "0.1": 151.2, "0.5": 168.5, "0.9": 186.0 }
}
Deciles only — and why
  • Accepted values are the nine deciles 0.1, 0.2, … 0.9. The API rejects anything else with a 422 rather than approximate it. The foundation models have exactly nine native decile heads. An interpolated value under a 0.95 label would be mislabeling, not extra precision.
  • A fan request does not change the point forecast. advanced-quantized always fits the full decile grid, regardless of the levels you request. So a fan request and a band-only request return the same point forecast.
  • model_info.quantile_crossings_fixed reports any case where a raw model returned a higher quantile below a lower one. The API repairs it by sorting. The API reports the repair rather than hide it.

What-If Adjustments (adjustments)

adjustments applies your own assumptions on top of the forecast. Examples are "assume we lose the enterprise deal" or "assume the promo lifts Q4 by 20%". This is the deliberate half of scenario planning. The probabilistic half is the band and the quantile fan.

{
  "adjustments": [
    {"type": "multiplier",  "value": 0.8,  "from_period": 3},
    {"type": "level_shift", "value": -500, "from_period": 6, "to_period": 9}
  ]
}
type
multiplier (scales the value) or level_shift (adds a constant)
value
The factor or the offset. A multiplier must be zero or greater.
from_period / to_period
An optional 1-based inclusive window. Omit both values to apply the adjustment across the whole horizon. Both values must fall within periods.
Order matters, and it is yours to choose
Adjustments compose in array order. On a value of 100, ×2 then +50 gives 250. +50 then ×2 gives 300. The response echoes both in adjusted.adjustments_applied.
The adjusted path is never stored
adjusted arrives as a sibling of result and carries "stored": false. The API persists and scores only the baseline forecast for accuracy. So your scenarios can never pollute your accuracy history. Every adjustment moves the point value, both bounds, and the whole quantile fan together. So the band always still brackets its own point.

Cumulative Totals (accumulate)

accumulate collapses the forecast path into a single total: Σ (value × survival × discount). The same operation covers cumulative demand over a lead time (safety stock), customer lifetime value, total cost of ownership, and net present value.

{
  "accumulate": {
    "decay": 0.95,
    "discount_rate": 0.10,
    "correlation": 0.5
  }
}
decay
Per-period survival factor, 0–1. 0.95 means 5% attrition each period. Default 1.0 (no decay).
discount_rate
Annual rate, 0–1, converted to your series frequency. A 10% annual rate on monthly periods discounts period 2 by 1.10-1/12, not by 1.10. Default 0.
correlation
How correlated the per-period errors are, from 0 to 1. This controls the width of the total's band only. Default 0.5. See below.

Period 1 is undiscounted and at full survival. by_period carries a running cumulative plus cumulative_lower/cumulative_upper, which is the number a safety-stock calculation actually reads.

The total's band rests on a stated assumption

A sum of each period's lower value does not give a lower bound for the total. It assumes every period misses low at the same time. Instead, the API states the joint assumption explicitly as one correlation parameter. The response echoes this parameter in accumulated.assumptions:

  • 0 — independent periods (the textbook √L rule). This is the narrowest band, and it is measurably too narrow. It covers only about 33–51% of outcomes against a nominal 80%, because a level error persists across a horizon.
  • 1 — perfectly correlated, exactly the same as a sum of the bounds. This is a legitimate conservative envelope, as long as you label it as one.
  • 0.5 (default) — measured across the benchmark suite as the tightest value that still covers at the stated rate on all three backends.

assumptions.measured tells you whether the correlation in force is the measured default or one you supplied. A value you pass is your assumption, and the API never labels it measured. When a model's band coverage is unknown, or any period lacks a band, lower and upper return null with an interval_reason. The API does not assemble a total from a partial set of variances.

Current-Period Completion (current_period)

Your data can end mid-period, for example daily sales sent on the 20th and forecast monthly. In that case, the API excludes the incomplete period from the model's history, because it would otherwise look like a sudden collapse. The forecast horizon then starts at that period with a full-period forecast. The API reports the exclusion in result.model_info.partial_period_dropped.

A full-period forecast is not the number you pace against a target. Part of the period has already happened. current_period: true adds the projection that accounts for this. The formula is projected_total = actual_to_date + full_period_forecast × (1 − elapsed_fraction):

RESPONSE (EXCERPT) 200 OK
{
  "result": { "forecasts": [ { "period": 1, "date": "2024-04-30", "forecast": 300.0, "...": "..." } ] },
  "current_period": {
    "applied": true,
    "period_start": "2024-04-01",
    "period_end": "2024-04-30",
    "last_actual_date": "2024-04-20",
    "elapsed_fraction": 0.6667,
    "actual_to_date": 200.0,
    "full_period_forecast": 300.0,
    "forecast_remainder": 100.0,
    "projected_total": 300.0,
    "lower": 283.3,
    "upper": 316.7,
    "assumptions": { "intraperiod_distribution": "uniform" },
    "stored": false
  }
}
actual_to_date
The sum of your own datapoints inside the current period. These are observed, not forecast.
elapsed_fraction
How much of the period you have observed, at your data's own resolution. A day you sent counts as a full day.
projected_total
The pacing number. This is what the current period is on track to reach.
lower / upper
The band covers only the unobserved remainder, so it tightens as the period fills. The API assumes a uniform spread across the period, as stated in assumptions.
stored
Always false. The projection mixes observed and forecast values, so the API never persists it or counts it toward forecast accuracy.
When it declines

The projection needs data finer than the forecast frequency. With one point per period, there is no way to tell a partial period from a complete one. If your data ends exactly on a period boundary, there is nothing to complete. In those cases, the block returns applied: false and a reason instead of a prorated guess. current_period works on POST /v2/forecast only. Batch and grouped requests reject it.

Covariates (covariates)

A covariate is a series that influences your data without being forecast itself. Promotions, public holidays, price changes, and upstream traffic are all covariates. You send them beside data, in the same shape, and the forecast is conditioned on them.

Covariates need "model": "multivariate". Every other model rejects the request. This is deliberate: the alternative is a forecast that looks conditioned on your promotion calendar while ignoring it, which is worse than a clear error.

{
  "identifier": "SKU-12345",
  "frequency": "M",
  "periods": 6,
  "model": "multivariate",
  "data": [
    {"date": "2024-01-01", "value": 100},
    {"date": "2024-02-01", "value": 180}
  ],
  "covariates": [
    {
      "name": "promo",
      "known_future": true,
      "data": [
        {"date": "2024-01-01", "value": 0},
        {"date": "2024-02-01", "value": 1},
        {"date": "2024-03-01", "value": 0}
      ]
    },
    {
      "name": "web_sessions",
      "data": [
        {"date": "2024-01-01", "value": 1200},
        {"date": "2024-02-01", "value": 1450}
      ]
    }
  ]
}

known_future

known_future says whether you know the covariate's future values. It decides how far the covariate has to reach:

known_future Example Must cover
false (default) Web sessions, competitor stock levels Your whole history
true A planned promotion, a holiday calendar Your whole history and every forecast period

A covariate that falls short of its range is rejected. A known_future covariate that stops at the end of your history is not known-future, and padding it would condition the forecast on values nobody supplied.

Send covariates at your data's frequency

A covariate recorded more finely than your target frequency is summed into each period, the same way your own data is. That is right for counts, such as promotion days per month or sessions per week.

It is wrong for levels, such as a price or a temperature, where summing daily values into a month is meaningless. Send those already at the frequency you forecast at, where nothing is summed.

The response reports which covariates were used in result.model_info.covariates, so you can confirm the forecast was conditioned rather than take it on trust. Up to 20 covariates per request. covariates works on POST /v2/forecast only. Batch and grouped requests reject it.

Model Options

Important Note About Models
ForecastAPI offers multiple models with varying levels of accuracy and computational cost. The advanced variants, the ensemble model and auto are more accurate but consume 25% more API usage than the standard model.

Choose the right forecasting model based on your accuracy requirements and budget. All models use the same automatic algorithm selection, but differ in their underlying implementation and computational complexity.

Available Models

Standard Model (Default)

This is the default forecasting model, and it balances accuracy and speed. It suits most use cases and gives reliable forecasts across a wide range of data patterns.

Performance
Fast processing time, suitable for real-time forecasting
API Usage Cost
1× standard rate

Best for:

  • General-purpose forecasting
  • High-volume batch processing
  • Real-time forecasting needs
  • Cost-sensitive applications
{
  "data": [...],
  "periods": 6,
  "frequency": "M"
  // model defaults to "standard" if not specified
}

Advanced Models (+25% cost)

These forecasting models aim for higher accuracy. They work well for new identifiers, smaller datasets, and complex patterns. Two variants exist:

  • advanced-quantized is the most thorough advanced variant. It evaluates and ensembles multiple candidate models per request for the highest single-model accuracy, at the cost of slower processing.
  • advanced-patched is a patch-based advanced variant. Its response times are comparable to the standard model. Choose it when you want advanced-level accuracy for real-time forecasting.

The API keeps the advanced value for backward compatibility. It behaves the same as advanced-quantized.

Performance Notice
The advanced-quantized variant processes slower than the standard model. Do not use it for real-time forecasting with large datasets. Use advanced-patched when response time matters.
Accuracy
Up to 30% improvement across accuracy scores
API Usage Cost
1.25× standard rate

Best for:

  • New products or identifiers with limited history
  • Small to medium datasets (5-50 datapoints)
  • Critical forecasts where accuracy matters most
  • Complex or irregular patterns
{
  "data": [...],
  "periods": 6,
  "frequency": "M",
  "model": "advanced-quantized"
}

// or for faster responses:
{
  "data": [...],
  "periods": 6,
  "frequency": "M",
  "model": "advanced-patched"
}

Multivariate Model (Covariates, +25% cost)

multivariate is the only model that reads covariates. Pick it when something outside the series drives it and you can measure that thing: a promotion calendar, a holiday flag, a price change, or upstream web sessions.

Without covariates it is an ordinary foundation model, and the other variants are the better tested choice. The reason to reach for it is the extra input, not extra accuracy on its own.

Longest horizon: 320 periods
multivariate forecasts at most 320 periods per request, where the other models go to 365. Ask for more and the request is rejected. It is never quietly shortened, so a response always covers the horizon you asked for.
{
  "data": [...],
  "periods": 6,
  "frequency": "M",
  "model": "multivariate",
  "covariates": [
    {
      "name": "promo",
      "known_future": true,
      "data": [
        {"date": "2024-01-01", "value": 0},
        {"date": "2024-02-01", "value": 1}
      ]
    }
  ]
}

Ensemble Model (Best Accuracy, +25% cost)

This is the most accurate model. It combines the standard model and both advanced variants into a single forecast. It smooths extreme outliers for more reliable forecasts.

Performance Notice
This is the slowest processing option. It suits batch processing or forecasting that is not time-sensitive.
Accuracy
17% improvement over advanced, 50%+ over standard
API Usage Cost
1.25× standard rate

Best for:

  • Mission-critical forecasts requiring highest accuracy
  • Data with extreme outliers or volatility
  • Strategic planning and long-term projections
  • Batch processing where speed is less critical
{
  "data": [...],
  "periods": 6,
  "frequency": "M",
  "model": "ensemble"
}

Auto Model (Learns Per Series, +25% cost)

auto routes each identifier to whichever model has proven most accurate on that series. A new identifier starts on the ensemble. As you send fresh actuals, the API scores every past forecast against what actually happened. It does this for the ensemble and for each member model individually. Once one model beats every alternative by a clear margin across enough matured forecasts, auto switches that identifier to it. While the evidence is thin, or the models are too close to call, the identifier stays on the ensemble.

The response always says which model answered and why, in model_info.auto_selection. It gives the selected model, the reason (no_history, insufficient_evidence, no_clear_winner, or scored_winner), and the per-model accuracy scores behind the decision.

It learns from your actuals
Routing evidence accrues when data arrives for dates you previously forecast. Keep sending fresh history for the same identifier and tenant_context as it becomes available. A series that you forecast once and never update stays on the ensemble.
Accuracy
Ensemble-level from the first call. It improves per series as evidence accrues.
API Usage Cost
1.25× standard rate

Best for:

  • Long-lived identifiers you forecast repeatedly (SKUs, tenants, metrics)
  • Catalogs where the best model differs per series
  • Integrations that should not hand-tune model choice per series
{
  "data": [...],
  "periods": 6,
  "frequency": "M",
  "identifier": "SKU-12345",
  "model": "auto"
}

API Usage Calculation

The advanced variants (advanced, advanced-quantized, advanced-patched), the multivariate model, the ensemble model and auto consume 25% more API usage than the standard model. This is how it works:

Example Calculation

10 calls · standard
10 × 1.00 = 10.0 usage
10 calls · advanced variant
10 × 1.25 = 12.5 usage
10 calls · ensemble
10 × 1.25 = 12.5 usage
10 calls · auto
10 × 1.25 = 12.5 usage — regardless of which model it routes to
Applies to All Endpoints
The model parameter and the usage multiplier apply to all forecasting endpoints, including /forecast, /batch, /traffic-forecasting, and /inventory-planning. In batches, the API applies the multiplier per series and honors per-series model overrides. The one exception is auto on /forecast/grouped. The API reconciles a hierarchy under one explicitly chosen model, so grouped requests do not accept it.

Quick Model Selection Guide

Use Case Recommended Model Reason
Recurring forecasts of long-lived series auto It learns the best model per series from realized accuracy
High-volume real-time forecasting standard It processes fast at a low cost
New product launch forecasting advanced It works better with limited historical data
Strategic planning & budgeting ensemble It gives the highest accuracy for critical decisions
Overnight batch processing ensemble Time matters less, and accuracy is key
Volatile or erratic data ensemble It smooths extreme outliers
Promotions, holidays or price changes drive the series multivariate It is the only model that reads covariates
Cost-sensitive applications standard It adds no usage cost

Error Responses

HTTP Status Codes

400
Bad Request — Invalid request parameters or malformed JSON
401
Unauthorized — Invalid or missing API key
429
Too Many Requests — Rate limit exceeded
500
Internal Server Error — Server error during forecast generation

Error Response Format

{
  "error": {
    "code": "invalid_data_format",
    "message": "Data array must contain at least 3 datapoints",
    "details": {
      "received_points": 2,
      "minimum_required": 3
    }
  }
}

Next Steps

LAST UPDATED — 14 AUG 2026 · FORECASTAPI DOCS
WAS THIS USEFUL? YES NO