Data Analysis

The /analyze endpoint inspects a single time series and returns a rich diagnostic profile — pattern type, trend, seasonality, intermittency, anomalies and a data-quality score — without generating a forecast. Use it to understand your data, validate it before forecasting, or surface insights in a dashboard.

Endpoint

POST /v2/analyze

Authentication

Bearer YOUR_API_KEY

Analyze Time Series

POST /analyze

Send your history exactly as you would to /forecast. The response contains two independent analyses of the same series:

result.generic

A fast, lightweight profile computed in-process. Always present, even for very short or sparse series. Great for quick heuristics and UI badges.

result.detailed

An in-depth analysis from the statistical engine — STL/ACF seasonality, distribution stats, and model recommendations. Richer, but needs a bit more history.

Common uses

  • Decide whether a series is forecastable (enough history, not too sparse) before spending a forecast call.
  • Pick a model or data_type based on detected pattern, trend and seasonality.
  • Show data-quality and confidence indicators to end users.
  • Flag anomalies, recent spikes and trend changes for review.

Request Body

{
  "identifier": "SKU-12345",
  "data": [
    {"date": "2022-01-01", "value": 118},
    {"date": "2022-02-01", "value": 126},
    {"date": "2022-03-01", "value": 141}
  ],
  "frequency": "M",
  "data_type": "sales",
  "periods": 6,
  "enable_intelligent_aggregation": true
}

Parameters

Parameter Type Required Description
identifier string Yes Unique label for the series (SKU, product ID, metric name). Echoed back as result.detailed.identifier.
data array Yes Array of { "date", "value" } points. All dates must share one format — either YYYY-MM-DD or YYYY-MM-DD HH:MM:SS — and every value must be numeric. Subject to the same per-plan data-point limits as /forecast.
frequency string Yes The spacing of your data: H, D, W, M, MS, ME, Q, or Y.
data_type string Yes The kind of series — e.g. sales, demand, inventory, web_traffic, revenue, cpu_usage, api_requests, or any custom string. Drives the data_characteristics block and model recommendations.
periods integer Yes Forecast horizon (≥ 1). Required by the shared request schema; accepted but does not change the analysis output — this endpoint never produces a forecast. Pass the horizon you intend to forecast with later.
start_date string No Optional anchor date (YYYY-MM-DD) used when aligning your points to the frequency grid.
tenant_context string No Optional tag for multi-tenant scoping. Passed through untouched.
enable_intelligent_aggregation boolean No Default true. Rolls finer-grained points up to frequency before analysis. Affects total_periods vs original_periods and the data_info reduction metrics, and relaxes the irregular-interval validation check.

The endpoint also accepts the shared forecasting options (model, selection_metric, confidence_level and the enable_* tuning flags) for schema compatibility, but they have no effect here — analysis always runs on the standard statistical engine.

Response

{
  "result": {
    "generic": {
      "pattern_type": "regular",
      "characteristics": {
        "total_periods": 24,
        "original_periods": 24,
        "non_zero_events": 24,
        "non_zero_ratio": 1,
        "mean_value": 142.6,
        "std_value": 21.4,
        "coefficient_of_variation": 0.15,
        "min_value": 105,
        "max_value": 188,
        "avg_interval": 1,
        "cv_non_zero_values": 0.15,
        "mean_non_zero_value": 142.6,
        "recent_pattern_change": false
      },
      "anomalies": {
        "anomaly_count": 0,
        "anomaly_ratio": 0,
        "has_recent_spike": false,
        "recent_spike_ratio": 1.04,
        "max_spike_ratio": 1.32,
        "upper_bound": 205.5,
        "lower_bound": 79.5
      },
      "trend": {
        "correlation": 0.72,
        "has_trend": true,
        "changepoint_count": 1,
        "has_recent_change": false
      },
      "seasonality": {
        "potential_seasonality": true,
        "detected_periods": [12],
        "has_seasonality": true
      },
      "data_quality": {
        "completeness_score": 1,
        "reliability_score": 0.95,
        "issues_detected": [],
        "forecast_confidence": "high",
        "confidence_by_horizon": {
          "short_term": 1,
          "medium_term": 0.95,
          "long_term": 0.75
        }
      },
      "insights": {
        "volatility_level": "low",
        "predictability": "high",
        "data_maturity": "sufficient",
        "seasonal_strength": 0.34,
        "recommended_horizon": 12
      },
      "warnings": []
    },
    "detailed": {
      "identifier": "SKU-12345",
      "analysis": {
        "data_validation": {
          "valid": true,
          "issues": [],
          "quality_score": 0.95,
          "quality_factors": {
            "data_sufficiency": 0.8,
            "completeness": 1,
            "variance": 1,
            "non_negative": 1
          },
          "data_points": 24,
          "date_range_days": 700
        },
        "basic_statistics": {
          "count": 24,
          "mean": 142.6,
          "median": 140.5,
          "std": 21.4,
          "min": 105,
          "max": 188,
          "cv": 0.15,
          "skewness": 0.21,
          "kurtosis": -0.44,
          "q25": 126.75,
          "q75": 158.25,
          "iqr": 31.5
        },
        "intermittency": {
          "zero_ratio": 0,
          "cv2": 0.023,
          "adi": 1,
          "avg_interval": 1,
          "is_intermittent": false,
          "demand_pattern": "smooth"
        },
        "seasonality": {
          "has_seasonality": true,
          "seasonal_strength": 0.34,
          "trend_strength": 0.61,
          "seasonal_periods": [12],
          "primary_period": 12,
          "stl_analysis": { "note": "raw STL decomposition output" },
          "acf_pacf_analysis": { "note": "raw ACF / PACF output" },
          "recommendation": {
            "model_suggestions": ["AutoETS", "AutoARIMA", "SeasonalNaive"],
            "use_seasonal_models": true,
            "suggested_periods": 12
          }
        },
        "trend_analysis": {
          "has_trend": true,
          "trend_direction": "increasing",
          "trend_strength": 0.72,
          "trend_significance": 0.72,
          "slope": 1.83,
          "trend_consistency": 0.68,
          "correlation_with_time": 0.72
        },
        "pattern_classification": {
          "pattern_type": "seasonal_trending",
          "pattern_characteristics": ["increasing trend", "seasonal"],
          "complexity_level": "medium",
          "complexity_score": 2,
          "forecasting_difficulty": "easy",
          "is_intermittent": false,
          "has_seasonality": true,
          "has_trend": true,
          "recommended_approach": {
            "primary_methods": ["AutoETS", "AutoARIMA", "MSTL"],
            "reasoning": "Complex seasonal trending patterns need advanced seasonal methods"
          }
        },
        "data_characteristics": {
          "intermittent_likely": true,
          "seasonal_patterns": ["weekly", "monthly", "yearly"],
          "trend_common": true,
          "outliers_common": true,
          "preferred_models": ["croston", "ses", "ets"]
        },
        "external_features": {
          "has_regressors": false,
          "regressor_cols": [],
          "regressor_count": 0
        }
      },
      "data_info": {
        "original_data_points": 24,
        "aggregated_points": 24,
        "aggregation_enabled": true,
        "data_reduction_ratio": 0,
        "date_range": { "start": "2022-01-01", "end": "2023-12-01" },
        "value_stats": {
          "min": 105,
          "max": 188,
          "mean": 142.6,
          "std": 21.4,
          "zero_count": 0,
          "non_zero_count": 24
        }
      }
    }
  },
  "meta": {
    "timing": { "validation": 6.4, "analysing": 214.8, "total": 221.2 }
  }
}

Two pattern taxonomies. result.generic.pattern_type describes intermittency only (Syntetos–Boylan), while result.detailed.analysis.pattern_classification.pattern_type combines trend, seasonality and intermittency into a broader label. The two can legitimately differ for the same series.

Response Fields — result.generic

The lightweight profile. Every field is always present (values fall back sensibly for tiny or all-zero series).

Top level

Field Type Description
pattern_type string Overall demand shape. One of regular, smooth_intermittent, erratic_intermittent, erratic_regular, no_values, or no_data.
characteristics object Core descriptive statistics (see below).
anomalies object Outlier and spike detection (see below).
trend object Trend direction and stability (see below).
seasonality object Detected seasonal periods (see below).
data_quality object Reliability scoring and forecast confidence (see below).
insights object Human-friendly summary indicators (see below).
warnings array<string> Human-readable caveats (recent spike, extreme values, recent trend change). Empty when none apply.

generic.characteristics

Field Type Description
total_periodsintegerNumber of periods after aggregation — what a forecast would actually use.
original_periodsintegerNumber of raw points you submitted.
non_zero_eventsintegerCount of values greater than zero.
non_zero_ratiofloatFraction of points with activity (0–1). Low values indicate sparse data.
mean_value / std_valuefloatMean and standard deviation of the raw values.
coefficient_of_variationfloatstd ÷ mean — relative volatility. Higher means noisier data.
min_value / max_valuefloatSmallest and largest observed values.
avg_intervalfloatAverage gap (in periods) between non-zero values. Large/infinite when the series is all zeros.
cv_non_zero_valuesfloatCoefficient of variation computed over only the non-zero values.
mean_non_zero_valuefloatAverage of the non-zero values only.
recent_pattern_changebooleanTrue when the most recent window jumped sharply versus earlier history.

generic.anomalies

Field Type Description
anomaly_countintegerPoints falling outside the 1.5×IQR fences.
anomaly_ratiofloatAnomalies as a fraction of all points (0–1).
has_recent_spikebooleanTrue when the last ~10% of data averages more than 2× or less than 0.5× the overall mean.
recent_spike_ratiofloatRecent-window mean ÷ overall mean.
max_spike_ratiofloatLargest value ÷ overall mean.
upper_bound / lower_boundfloatThe IQR outlier fences used to flag anomalies.

generic.trend

Field Type Description
correlationfloatCorrelation of value against time, −1 to 1. Positive means rising, negative means falling.
has_trendbooleanTrue when |correlation| > 0.3.
changepoint_countintegerNumber of detected level shifts in the series.
has_recent_changebooleanTrue when a changepoint falls in the last 30% of the series.

generic.seasonality

Field Type Description
potential_seasonalitybooleanTrue when there are at least 12 points and non-zero variance — enough to look for seasonality.
detected_periodsarray<integer>Lags whose autocorrelation exceeds 0.5 (checks 12, 4 and 7 — e.g. yearly, quarterly, weekly).
has_seasonalitybooleanTrue when the measured seasonal strength exceeds 0.15.

generic.data_quality

Field Type Description
completeness_scorefloatHow much of the series carries signal (equals non_zero_ratio), 0–1.
reliability_scorefloatOverall trust score (0–1), penalized for volatility, anomalies and frequent changepoints.
issues_detectedarray<string>Any of sparse_data, high_anomaly_rate, recent_anomalies, unstable_trends, recent_trend_change.
forecast_confidencestringMapped from reliability_score: high, medium, low, or very_low.
confidence_by_horizonobjectConfidence (0–1) for short_term (1–3), medium_term (4–12) and long_term (12+) horizons.

generic.insights

Field Type Description
volatility_levelstringlow (CV ≤ 0.5), medium (≤ 1), or high (> 1).
predictabilitystringhigh, medium, or low, from volatility, anomaly rate and trend clarity.
data_maturitystringlimited (< 24 points) or sufficient (≥ 24).
seasonal_strengthfloatStrength of the strongest detected seasonal cycle, 0–1.
recommended_horizonintegerSuggested number of periods to forecast (1–24), scaled by reliability, pattern and history length.

Response Fields — result.detailed

The in-depth analysis. It wraps the echoed identifier, an analysis object with several sub-analyses, and a data_info block describing what was actually analyzed. If the statistical engine fails, detailed instead carries an error object (see Errors).

detailed.analysis.data_validation

Field Type Description
validbooleanTrue when no quality issues were found.
issuesarray<string>Detected problems, e.g. missing values, negative values, zero variance, too few points, or irregular intervals.
quality_scorefloatComposite 0–1 score, the mean of the four quality_factors.
quality_factorsobjectdata_sufficiency (points ÷ 30, capped at 1), completeness, variance, and non_negative, each 0–1.
data_pointsintegerNumber of points analyzed.
date_range_daysintegerCalendar span between the first and last point, in days.

detailed.analysis.basic_statistics

Field Type Description
countintegerNumber of values.
mean / median / stdfloatCentral tendency and dispersion.
min / maxfloatExtremes.
cvfloatCoefficient of variation (std ÷ mean).
skewnessfloatDistribution asymmetry. 0 is symmetric; positive means a long right tail.
kurtosisfloatExcess kurtosis (tailed-ness relative to normal; 0 is normal).
q25 / q75 / iqrfloat25th and 75th percentiles and their difference (interquartile range).

detailed.analysis.intermittency

Field Type Description
zero_ratiofloatProportion of zero values (0–1).
cv2floatSquared coefficient of variation. > 0.49 signals erratic demand size.
adifloatAverage Demand Interval — mean gap between non-zero demands. > 1.32 signals intermittent demand.
avg_intervalfloatAlias of adi, kept for backward compatibility.
is_intermittentbooleanTrue when zeros are frequent or demands are widely spaced.
demand_patternstringSyntetos–Boylan class: smooth, intermittent, erratic, or lumpy.

detailed.analysis.seasonality

Field Type Description
has_seasonalitybooleanTrue when STL or ACF/PACF detected a repeating cycle.
seasonal_strengthfloatSTL seasonal strength, 0–1.
trend_strengthfloatSTL trend strength, 0–1.
seasonal_periodsarray<integer>All detected cycle lengths (deduplicated, sorted).
primary_periodinteger | nullThe dominant cycle length, or null if none stands out.
stl_analysis / acf_pacf_analysisobjectThe raw per-method outputs behind the combined result, for callers that want the details.
recommendationobjectmodel_suggestions (array), use_seasonal_models (bool), and suggested_periods (int, array, or null).

detailed.analysis.trend_analysis

Field Type Description
has_trendbooleanTrue when trend_strength > 0.3.
trend_directionstringincreasing, decreasing, or none.
trend_strength / trend_significancefloatAbsolute correlation of value with time, 0–1.
slopefloatBest-fit linear slope — average change per period.
trend_consistencyfloatHow linear the trend is (0–1); higher means a cleaner straight line.
correlation_with_timefloatSigned correlation, −1 to 1.

detailed.analysis.pattern_classification

Field Type Description
pattern_typestringCombined label, e.g. regular, trending, seasonal, seasonal_trending, smooth_intermittent, erratic_intermittent, intermittent_seasonal, intermittent_trending, or complex_intermittent_seasonal_trending.
pattern_characteristicsarray<string>Plain-language traits, e.g. "increasing trend", "seasonal".
complexity_levelstringlow, medium, or high.
complexity_scoreintegerNumeric score behind complexity_level.
forecasting_difficultystringeasy, medium, or hard.
is_intermittent / has_seasonality / has_trendbooleanThe three signals folded into the classification.
recommended_approachobjectprimary_methods (array) plus a reasoning string for the recommendation.

detailed.analysis.data_characteristics & external_features

Field Type Description
data_characteristicsobjectStatic priors for your data_type: intermittent_likely, seasonal_patterns, trend_common, outliers_common, and preferred_models. These describe what's typical for the type, not what was measured in your series.
external_featuresobjecthas_regressors (bool), regressor_cols (array) and regressor_count (int). Zero/empty unless external features were supplied.

detailed.data_info

Field Type Description
original_data_pointsintegerPoints received after parsing.
aggregated_pointsintegerPoints remaining after intelligent aggregation.
aggregation_enabledbooleanWhether aggregation ran (mirrors enable_intelligent_aggregation).
data_reduction_ratiofloatPercentage of points removed by aggregation (0 when nothing was rolled up).
date_rangeobjectstart and end dates (YYYY-MM-DD) of the analyzed series.
value_statsobjectmin, max, mean, std, zero_count, and non_zero_count of the analyzed values.

Response Fields — meta

Field Type Description
meta.timing.validationfloatTime spent validating the request, in milliseconds.
meta.timing.analysingfloatTime spent running the analysis, in milliseconds.
meta.timing.totalfloatEnd-to-end request time, in milliseconds.

Errors

HTTP Status Codes

401
Unauthorized
Invalid or missing API key.
422
Unprocessable Entity
Validation failed — e.g. malformed dates, non-numeric values, a missing required field, or more data points than your plan allows.
429
Too Many Requests
Rate limit exceeded.
500
Internal Server Error
Unexpected error while analyzing the series.

Validation Error Format

{
  "message": "Data points exceed the maximum allowed for your current plan (1000).",
  "errors": {
    "data": ["Data points exceed the maximum allowed for your current plan (1000)."]
  }
}

Partial Failure

The two analyses are computed independently. If the statistical engine is unavailable or errors on your data, the request still returns 200 with a populated result.generic, while result.detailed carries a structured error object instead of the analysis. Always check for it before reading detailed fields.

{
  "result": {
    "generic": { "pattern_type": "regular" },
    "detailed": {
      "error": {
        "source": "statsforecast",
        "code": "statsforecast_empty_payload",
        "message": "StatsForecast analyze returned no analysis results",
        "details": {}
      }
    }
  },
  "meta": {
    "timing": { "validation": 5.1, "analysing": 42.0, "total": 47.4 }
  }
}

Next Steps