{
  "openapi": "3.1.1",
  "info": {
    "title": "Time-Series Explorer API",
    "version": "1.0.0",
    "summary": "Resample, smooth and decompose date/value data: trend, R², seasonality, YoY change and anomalies.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://timeseriesexplorer.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Resample, smooth and decompose date/value data: trend, R², seasonality, YoY change and anomalies.",
        "description": "Upload or paste dated values (one or several series, wide or long format). The explorer resamples to days, weeks, months, quarters or years, adds moving averages (simple, exponential or centred), fits a linear trend with R², estimates weekday or month-of-year seasonality, computes period and year-over-year change and flags anomalies by z-score or IQR, with charts, tables and a CSV export.\n\nMethodology:\n- Dates are parsed per column (ISO, European and US formats, month names, YYYY-MM, YYYY-Qn); rows without a date or number are skipped and counted. Values with the same timestamp are combined with the chosen method.\n- Resampling groups values into calendar periods in UTC (weeks start on Monday, ISO 8601) and combines them by average, sum, first, last, minimum, maximum or median. Empty periods are left out rather than filled.\n- Moving averages: simple = mean of the last w points; exponential = α·value + (1−α)·previous with α = 2/(w+1); centred = mean of w points around each point (2×w for even windows, as in classical decomposition).\n- Trend: ordinary least squares of value on time in days; R² is the share of variance the straight line explains. The slope is also shown per year (× 365.25).\n- Seasonality (an \"STL-lite\" classical additive decomposition): trend = centred moving average over one period (7 weekdays, 12 months or 4 quarters); seasonal index = average detrended value per weekday/month/quarter, shifted to sum to zero; strength = 1 − var(remainder) / var(detrended), reported once at least two full periods are available. It is a simplification of STL (no LOESS, fixed seasonal pattern).\n- Anomalies are judged on residuals: the decomposition remainder when seasonality is estimated, otherwise the deviation from a centred moving average. z-score flags |z| above the threshold; IQR flags values outside Q1 − k·IQR … Q3 + k·IQR. Anomalies are statistical outliers, not proof of errors.\n- Year-over-year compares the last point with the point on the same calendar date (± a few days, depending on spacing) one year earlier.",
        "tags": [
          "charts"
        ],
        "x-mcp-tool": "time_series_explorer",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimeSeriesExplorerInput"
              },
              "examples": {
                "example1": {
                  "summary": "Daily visits: weekday pattern, trend and two anomalies",
                  "value": {
                    "data": "date,visits\n2026-01-05,98\n2026-01-06,104.5\n2026-01-07,109\n2026-01-08,105.5\n2026-01-09,106\n2026-01-10,92.5\n2026-01-11,91\n2026-01-12,105.5\n2026-01-13,107\n2026-01-14,111.5\n2026-01-15,108\n2026-01-16,108.5\n2026-01-17,100\n2026-01-18,93.5\n2026-01-19,108\n2026-01-20,109.5\n2026-01-21,114\n2026-01-22,115.5\n2026-01-23,111\n2026-01-24,102.5\n2026-01-25,96\n2026-01-26,110.5\n2026-01-27,117\n2026-01-28,161.5\n2026-01-29,118\n2026-01-30,113.5\n2026-01-31,105\n2026-02-01,103.5\n2026-02-02,113\n2026-02-03,119.5\n2026-02-04,119\n2026-02-05,120.5\n2026-02-06,121\n2026-02-07,107.5\n2026-02-08,106\n2026-02-09,115.5\n2026-02-10,122\n2026-02-11,126.5\n2026-02-12,123\n2026-02-13,123.5\n2026-02-14,110\n2026-02-15,108.5\n2026-02-16,123\n2026-02-17,124.5\n2026-02-18,129\n2026-02-19,125.5\n2026-02-20,126\n2026-02-21,117.5\n2026-02-22,111\n2026-02-23,125.5\n2026-02-24,127\n2026-02-25,93.5\n2026-02-26,133\n2026-02-27,128.5\n2026-02-28,120\n2026-03-01,113.5\n2026-03-02,128\n2026-03-03,134.5\n2026-03-04,134\n2026-03-05,135.5\n2026-03-06,131\n2026-03-07,122.5\n2026-03-08,121\n2026-03-09,130.5\n2026-03-10,137\n2026-03-11,136.5\n2026-03-12,138\n2026-03-13,138.5\n2026-03-14,125\n2026-03-15,123.5",
                    "smoothing": "sma",
                    "window": 7,
                    "png": false
                  }
                },
                "example2": {
                  "summary": "Two products, monthly: seasonality and year-over-year",
                  "value": {
                    "data": "month,Product A,Product B\n2024-01,120,80\n2024-02,115,82\n2024-03,130,85\n2024-04,142,90\n2024-05,150,94\n2024-06,170,99\n2024-07,182,101\n2024-08,176,104\n2024-09,150,108\n2024-10,138,110\n2024-11,125,115\n2024-12,160,121\n2025-01,131,124\n2025-02,126,126\n2025-03,143,131\n2025-04,151,133\n2025-05,166,138\n2025-06,188,141\n2025-07,197,147\n2025-08,190,150\n2025-09,163,152\n2025-10,150,158\n2025-11,139,161\n2025-12,176,169",
                    "smoothing": "centred",
                    "window": 12,
                    "seasonality": "month",
                    "anomalyMethod": "iqr",
                    "png": true
                  }
                },
                "example3": {
                  "summary": "Daily data resampled to weekly sums",
                  "value": {
                    "data": "date,visits\n2026-01-05,98\n2026-01-06,104.5\n2026-01-07,109\n2026-01-08,105.5\n2026-01-09,106\n2026-01-10,92.5\n2026-01-11,91\n2026-01-12,105.5\n2026-01-13,107\n2026-01-14,111.5\n2026-01-15,108\n2026-01-16,108.5\n2026-01-17,100\n2026-01-18,93.5\n2026-01-19,108\n2026-01-20,109.5\n2026-01-21,114\n2026-01-22,115.5\n2026-01-23,111\n2026-01-24,102.5\n2026-01-25,96\n2026-01-26,110.5\n2026-01-27,117\n2026-01-28,161.5\n2026-01-29,118\n2026-01-30,113.5\n2026-01-31,105\n2026-02-01,103.5\n2026-02-02,113\n2026-02-03,119.5\n2026-02-04,119\n2026-02-05,120.5\n2026-02-06,121\n2026-02-07,107.5\n2026-02-08,106\n2026-02-09,115.5\n2026-02-10,122\n2026-02-11,126.5\n2026-02-12,123\n2026-02-13,123.5\n2026-02-14,110\n2026-02-15,108.5\n2026-02-16,123\n2026-02-17,124.5\n2026-02-18,129\n2026-02-19,125.5\n2026-02-20,126\n2026-02-21,117.5\n2026-02-22,111\n2026-02-23,125.5\n2026-02-24,127\n2026-02-25,93.5\n2026-02-26,133\n2026-02-27,128.5\n2026-02-28,120\n2026-03-01,113.5\n2026-03-02,128\n2026-03-03,134.5\n2026-03-04,134\n2026-03-05,135.5\n2026-03-06,131\n2026-03-07,122.5\n2026-03-08,121\n2026-03-09,130.5\n2026-03-10,137\n2026-03-11,136.5\n2026-03-12,138\n2026-03-13,138.5\n2026-03-14,125\n2026-03-15,123.5",
                    "resample": "week",
                    "resampleAgg": "sum",
                    "smoothing": "ema",
                    "window": 3,
                    "seasonality": "none",
                    "png": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TimeSeriesExplorerResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "TimeSeriesExplorerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "file": {
            "title": "CSV, TSV or Excel file",
            "description": "Max 20 MB. One date column plus one or more value columns (wide), or date, series and value columns (long). Upload as {filename, contentType?, contentBase64}. Max 20480 KiB. Accepts .csv,.tsv,.txt,.xlsx,text/csv,text/tab-separated-values,text/plain,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "data": {
            "title": "Or paste data",
            "description": "First row = headers. Dates like 2026-03-01, 01.03.2026, 03/01/2026, 2026-03 or Mar 2026.",
            "type": "string",
            "maxLength": 2000000
          },
          "sheet": {
            "title": "Excel sheet",
            "type": "string",
            "maxLength": 120
          },
          "dateColumn": {
            "title": "Date column",
            "description": "Empty: the first date column.",
            "type": "string",
            "maxLength": 200
          },
          "valueColumns": {
            "title": "Value columns",
            "description": "Comma-separated; empty: all numeric columns (max 8).",
            "type": "string",
            "maxLength": 1000
          },
          "seriesColumn": {
            "title": "Series column (long format)",
            "description": "Optional: a column naming the series of each row; then the first value column holds the values.",
            "type": "string",
            "maxLength": 200
          },
          "resample": {
            "title": "Resample to",
            "type": "string",
            "enum": [
              "auto",
              "day",
              "week",
              "month",
              "quarter",
              "year"
            ],
            "default": "auto"
          },
          "resampleAgg": {
            "title": "Combine values in a period by",
            "type": "string",
            "enum": [
              "mean",
              "sum",
              "last",
              "first",
              "min",
              "max",
              "median"
            ],
            "default": "mean"
          },
          "smoothing": {
            "title": "Moving average",
            "type": "string",
            "enum": [
              "sma",
              "ema",
              "centred",
              "none"
            ],
            "default": "sma"
          },
          "window": {
            "title": "Moving-average window",
            "type": "integer",
            "minimum": 2,
            "maximum": 400,
            "default": 7,
            "x-unit": "points"
          },
          "seasonality": {
            "title": "Seasonality",
            "type": "string",
            "enum": [
              "auto",
              "weekday",
              "month",
              "quarter",
              "none"
            ],
            "default": "auto"
          },
          "anomalyMethod": {
            "title": "Anomaly detection",
            "type": "string",
            "enum": [
              "zscore",
              "iqr",
              "none"
            ],
            "default": "zscore"
          },
          "threshold": {
            "title": "Anomaly threshold",
            "description": "z-score: flag |z| above this (default 3). IQR: fences at k × IQR (default 1.5).",
            "type": "number",
            "minimum": 0.5,
            "maximum": 10
          },
          "png": {
            "title": "Also create a PNG of the main chart",
            "type": "boolean",
            "default": true
          }
        },
        "required": []
      },
      "TimeSeriesExplorerResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "dateColumn",
              "unit",
              "resampled",
              "smoothing",
              "window",
              "anomalyMethod",
              "threshold",
              "series",
              "svg",
              "png"
            ],
            "properties": {
              "dateColumn": {
                "type": "string"
              },
              "unit": {
                "type": "string",
                "description": "Spacing of the analysed series."
              },
              "resampled": {
                "type": "boolean"
              },
              "smoothing": {
                "type": "string"
              },
              "window": {
                "type": "integer"
              },
              "anomalyMethod": {
                "type": "string"
              },
              "threshold": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "series": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "name",
                    "points",
                    "start",
                    "end",
                    "first",
                    "last",
                    "min",
                    "max",
                    "mean",
                    "totalChangePercent",
                    "lastPeriodChangePercent",
                    "yoyChangePercent",
                    "trend",
                    "seasonality",
                    "anomalies"
                  ],
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "points": {
                      "type": "integer"
                    },
                    "start": {
                      "type": "string"
                    },
                    "end": {
                      "type": "string"
                    },
                    "first": {
                      "type": "number"
                    },
                    "last": {
                      "type": "number"
                    },
                    "min": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "date",
                        "value"
                      ],
                      "properties": {
                        "date": {
                          "type": "string"
                        },
                        "value": {
                          "type": "number"
                        }
                      }
                    },
                    "max": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "date",
                        "value"
                      ],
                      "properties": {
                        "date": {
                          "type": "string"
                        },
                        "value": {
                          "type": "number"
                        }
                      }
                    },
                    "mean": {
                      "type": "number"
                    },
                    "totalChangePercent": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "lastPeriodChangePercent": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "yoyChangePercent": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Last value vs the value one year earlier (null when the series is shorter than a year)."
                    },
                    "trend": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "additionalProperties": false,
                      "required": [
                        "slopePerDay",
                        "slopePerYear",
                        "r2",
                        "startValue",
                        "endValue"
                      ],
                      "properties": {
                        "slopePerDay": {
                          "type": "number"
                        },
                        "slopePerYear": {
                          "type": "number"
                        },
                        "r2": {
                          "type": "number"
                        },
                        "startValue": {
                          "type": "number"
                        },
                        "endValue": {
                          "type": "number"
                        }
                      }
                    },
                    "seasonality": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "additionalProperties": false,
                      "required": [
                        "kind",
                        "strength",
                        "phases"
                      ],
                      "properties": {
                        "kind": {
                          "type": "string"
                        },
                        "strength": {
                          "type": [
                            "number",
                            "null"
                          ]
                        },
                        "phases": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "additionalProperties": false,
                            "required": [
                              "phase",
                              "index",
                              "average",
                              "count"
                            ],
                            "properties": {
                              "phase": {
                                "type": "string"
                              },
                              "index": {
                                "type": [
                                  "number",
                                  "null"
                                ]
                              },
                              "average": {
                                "type": [
                                  "number",
                                  "null"
                                ]
                              },
                              "count": {
                                "type": "integer"
                              }
                            }
                          }
                        }
                      }
                    },
                    "anomalies": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "date",
                          "value",
                          "expected",
                          "score"
                        ],
                        "properties": {
                          "date": {
                            "type": "string"
                          },
                          "value": {
                            "type": "number"
                          },
                          "expected": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "score": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "svg": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artifactId",
                  "filename",
                  "mime",
                  "bytes",
                  "sha256",
                  "path"
                ],
                "properties": {
                  "artifactId": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime": {
                    "type": "string"
                  },
                  "bytes": {
                    "type": "integer"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "path": {
                    "type": "string",
                    "description": "Download path on the same site (valid for about 2 hours)."
                  }
                }
              },
              "png": {
                "anyOf": [
                  {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "artifactId",
                      "filename",
                      "mime",
                      "bytes",
                      "sha256",
                      "path"
                    ],
                    "properties": {
                      "artifactId": {
                        "type": "string"
                      },
                      "filename": {
                        "type": "string"
                      },
                      "mime": {
                        "type": "string"
                      },
                      "bytes": {
                        "type": "integer"
                      },
                      "sha256": {
                        "type": "string"
                      },
                      "path": {
                        "type": "string",
                        "description": "Download path on the same site (valid for about 2 hours)."
                      }
                    }
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}