Developer Docs: /v1/forecast and /v1/clean are live; create API Keys self-service on the SequenxAI Platform.

POST /v1/clean

POST https://api.sequenxai.com/v1/clean is the free cleaning dry run (Beta). It runs the same cleaning stage as /v1/forecast and returns only the cleaning report and the effective policy: no model call, no forecast. Use it to confirm how your data will be treated before forecasting, and to collect the decision ids you can pass to accept / decline.

1. Request examples

clean.sh
# 12 hourly slots; 06:00 is missing. Free dry run: no model call, no points billed.
curl -sS -X POST "https://api.sequenxai.com/v1/clean" \
  -H "Authorization: Bearer $SEQUENX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "freq": "1h",
    "series": [
      {"unique_id": "meter-001", "ds": "2026-09-01T00:00:00Z", "y": 142.5},
      {"unique_id": "meter-001", "ds": "2026-09-01T01:00:00Z", "y": 141.8},
      {"unique_id": "meter-001", "ds": "2026-09-01T02:00:00Z", "y": 140.9},
      {"unique_id": "meter-001", "ds": "2026-09-01T03:00:00Z", "y": 139.7},
      {"unique_id": "meter-001", "ds": "2026-09-01T04:00:00Z", "y": 139.2},
      {"unique_id": "meter-001", "ds": "2026-09-01T05:00:00Z", "y": 138.8},
      {"unique_id": "meter-001", "ds": "2026-09-01T07:00:00Z", "y": 138.2},
      {"unique_id": "meter-001", "ds": "2026-09-01T08:00:00Z", "y": 139.0},
      {"unique_id": "meter-001", "ds": "2026-09-01T09:00:00Z", "y": 140.4},
      {"unique_id": "meter-001", "ds": "2026-09-01T10:00:00Z", "y": 141.3},
      {"unique_id": "meter-001", "ds": "2026-09-01T11:00:00Z", "y": 142.0}
    ]
  }'

2. Request body

FieldTypeRequiredDefaultNotes
seriesarrayYes—Long-table rows as in /v1/forecast, at least 1 and at most 200,000 per request.
freqstringNoInferredAs in /v1/forecast.
clean_seriesbooleanNotruefalse detects without repairing and returns 422 as soon as a repair is needed.
modelstringNo—When given, that model's minimum length is checked (SERIES_TOO_SHORT) and an entity longer than its maximum context returns 422 SERIES_TOO_LONG, as /v1/forecast would; history is never cut. Same values as /v1/forecast.
optionsobjectNo—Same as /v1/forecast: timezone, ds_format, gap_split_min_steps, cleaning.preset / version / accept / decline, and the 2026-11 knobs; unknown fields inside options.cleaning return 422 VALIDATION_ERROR. Use it to preview policy version 2026-11 for free.

Forecast fields such as h, level or future_covariates do not exist here; sending them returns 422 VALIDATION_ERROR because they are outside the contract.

3. Limits and billing

ItemRule
BillingFree. Each call is recorded as a usage row with zero billable points.
Daily point quotaNot consumed.
Rate limit60 calls per minute per account (sliding window shared by all its keys), counted separately from /v1/forecast; above that, 429 with X-RateLimit-Policy: clean-rpm.
Shared capacityCleaning capacity is shared by all accounts and dry runs never queue: while it is full, 503 SERVICE_UNAVAILABLE with Retry-After; retry after that interval.
Rows per requestAt most 200,000, otherwise 422 POINTS_EXCEEDED (an oversized call is refused, never queued); the same code is returned when the regular grid implied by the series is too large.
raw presetNot accepted: raw skips cleaning, so there is no report to show; returns 422 CLEANING_OPTION_UNSUPPORTED (details.field is options.cleaning.preset).

4. Response fields

FieldNotes
request_idServer request ID, same as the X-Request-Id header.
freqYour freq as sent; when omitted, the inferred canonical token.
cleaning_reportThe cleaning_report/1 structured report, described in Cleaning Presets & Report.
effective_policy{id, preset, version, source}, as in /v1/forecast.

5. Response example

The response to the curl request in section 1, with the series[] detail abridged:

clean-response.json
{
  "request_id": "req_9c2e7b1d4a8f4f3e9d6c5b4a3f2e1d0c",
  "freq": "1h",
  "cleaning_report": {
    "schema_version": "cleaning_report/1",
    "policy": {
      "id": "default@2026-13",
      "clean_series": true,
      "duplicate_policy": "last",
      "gap_fill_max_steps": 3,
      "gap_split_min_steps": null,
      "timezone_default": "UTC",
      "accept": [],
      "decline": [],
      "...": "..."
    },
    "overall": {
      "series_count": 1,
      "input_points": 11,
      "output_points": 12,
      "health_score": 84,
      "health_tier": "Healthy",
      "forecastable": true
    },
    "series": ["... per-series input / freq / issues / actions / metrics / timezone ..."],
    "warnings": [],
    "blocking": [],
    "decisions": [
      {
        "id": "grid.regrid:meter-001",
        "rule": "grid.regrid",
        "class": "structural",
        "status": "applied",
        "unique_id": "meter-001",
        "action": "insert_empty_slots",
        "before": {"slots": 11},
        "after": {"slots": 12},
        "reversible": false,
        "evidence": {}
      },
      {
        "id": "gap.impute:meter-001",
        "rule": "gap.impute",
        "class": "fabricate",
        "status": "applied",
        "unique_id": "meter-001",
        "action": "pchip",
        "before": {"missing": 1},
        "after": {"filled": 1},
        "reversible": false,
        "evidence": {}
      }
    ]
  },
  "effective_policy": {
    "id": "default@2026-13",
    "preset": "default",
    "version": "2026-13",
    "source": "platform"
  }
}

6. When cleaning blocks

When the data cannot be forecast safely under the current policy, /v1/clean returns the same 422 code as /v1/forecast would, with the full report in error.details.cleaning_report. blocking[] says why:

clean-blocked.json
{
"error": {
  "code": "SERIES_DISCONTINUOUS",
  "message": "A gap ending shortly before the last observation is too long to fill reliably; the forecast would start from filled values. Send the missing recent values, or send only the history before the gap",
  "request_id": "req_…",
  "details": {
    "cleaning_report": {
      "schema_version": "cleaning_report/1",
      "overall": {"forecastable": false, "...": "..."},
      "blocking": [
        {"code": "health_critical", "unique_id": "meter-001", "count": null, "missing_ratio": 0.119048,
         "reason": "gap_near_origin_too_long", "reasons": ["gap_near_origin_too_long"],
         "period": 24, "near_gap_steps": 20, "far_gap_steps": 0}
      ],
      "decisions": ["..."]
    }
  }
}
}

How to resolve each code is in Error Codes.

7. How it relates to /v1/forecast

  • With the same series, freq, model and options, both produce the same cleaning result and decision ids, so ids seen in the dry run can go straight into options.cleaning.accept / decline on /v1/forecast.
  • Without model, the dry run checks neither the minimum length nor the maximum context; pass the same model to see exactly the report (or the SERIES_TOO_LONG rejection) the forecast will produce.
  • The forecast additionally checks h and the daily quota; those are outside the dry run.