retryable is true for 409, 429, 500, 503 and 504, and false for everything else. The OpenAPI contract lists every code with its status, category and retryable under ErrorDetail → x-error-codes.
1. The error envelope
Every error is JSON with a single top-level error object:
error-response.json
{ "error": { "code": "HORIZON_EXCEEDS_MODEL", "category": "limit_exceeded", "retryable": false, "message": "h=1500 exceeds the maximum horizon 1024 of model 'hora-1.0'", "request_id": "req_0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a", "details": {"model": "hora-1.0", "h": 1500, "max_horizon": 1024} }}
Field
Notes
code
Machine-readable code. Branch on it; do not parse message.
category
Coarse group of code: invalid_request, data_invalid, limit_exceeded, model_unavailable, auth, conflict, rate_limited or server (see the table above). New values may be added; branch on code when you need the exact cause.
retryable
true when sending the same request again may succeed (409 IDEMPOTENCY_IN_PROGRESS, 429, 500, 503, 504); false when the request itself must change. A daily-quota 429 is true but only clears at the next UTC midnight.
message
Developer-facing English text; wording may change.
request_id
Server request ID, same as the X-Request-Id header; quote it to support.
details
Optional extra information that depends on the code, see the tables below.
Paths that are not open (for example /v1/anomaly-detection, /v1/batch/forecast) return a 404 without this envelope.
2. Request and authentication
Code
HTTP
When
What to do
MALFORMED_JSON
400
The body is not valid JSON.
Serialize with a JSON library instead of concatenating strings.
VALIDATION_ERROR
400
Request headers only (body problems are 422, below): Idempotency-Key is not 1–255 printable ASCII characters, or it differs from the deprecated X-Idempotency-Key sent alongside it (details.field names the header).
Use a conforming key, such as a UUID, and send only Idempotency-Key.
IDEMPOTENCY_KEY_REUSED
422
The same Idempotency-Key was sent with a different body within 24 hours. Nothing runs and nothing is billed.
Use a new key for a new request; to retry, send exactly the first body.
IDEMPOTENCY_IN_PROGRESS
409
The first request with the same Idempotency-Key is still running.
No Authorization, not Bearer <key>, unknown, deleted or revoked key, or a sqx_test_* test key.
Check that the key is still listed on the SequenxAI Platform; the live API accepts sqx_live_* only.
INSUFFICIENT_SCOPE
403
The key lacks the forecast:write scope.
Use a key created on the SequenxAI Platform (it carries the scope by default).
ACCOUNT_SUSPENDED (beta)
403
The key's account or workspace is suspended by SequenxAI; the key itself stays valid and works again once it is restored. Not billed.
Contact SequenxAI support.
ORG_PENDING_DELETION (beta)
403
The key's workspace is in its deletion cool-off period, so its API Keys are paused. Not billed.
Cancel the workspace deletion on the SequenxAI Platform; the keys work again as soon as it is cancelled.
VALIDATION_ERROR
422
Request body only (header problems are 400, above): a field has the wrong type or value, a top-level field is outside the contract, the body is not a JSON object, more than 8 covariate columns are sent, raw is used without freq, model_options has an unknown key, options.timezone is not a valid IANA name, or a ds value cannot be parsed.
Read details.field, or details.errors[] (each with field and message, at most 10).
FUTURE_COVARIATES_INVALID
422
A future_covariates row contains y, or lacks a string unique_id / ds.
Send only covariate columns in future_covariates; see Covariate Support.
COVARIATE_COLUMN_MISMATCH
422
A future_covariates column does not appear on the series rows; details.future_only lists them.
Add the column's history to series, or drop it from future_covariates.
POINTS_EXCEEDED
422
The request has more rows than the per-request cap (200,000 on /v1/forecast, counting series plus future_covariates; 200,000 on /v1/clean), or the regular grid implied by the series is too large. The cap follows the cleaning host's memory, so retrying the same request does not help.
Split the request; details carries points and cap.
3. Cleaning policy and model
Code
HTTP
When
What to do
CLEANING_OPTION_UNSUPPORTED
422
Unknown options.cleaning.preset or options.cleaning.version; raw combined with accept / decline or with cleaning parameters; cleaning parameters (impute_method … long_gap) under the deprecated version 2026-10, or a nonnegative value that version does not offer; raw on /v1/clean (including raw inherited from a key or organization default).
Presets are default, strict and raw; details.field names the field and details.allowed lists valid values. For parameters, details.requires_version names the version they need (or details.options and details.supported list the values on offer): omit version to use the default 2026-12. See the Cleaning Policy Reference.
MODEL_NOT_AVAILABLE
422
model (on /v1/forecast or /v1/clean) is anything other than hora-1.0 or its alias hora. Rejected before any quota is reserved; not billed.
Send hora-1.0 or omit model; details carries model (the id you sent) and available. See Models & Limits.
MODEL_OPTION_UNSUPPORTED
422
A model_options key is not taken by the selected model. hora-1.0 takes both nonnegative and flip_average, so this does not occur with it today. Not billed.
Drop the keys in details.unsupported; details carries model, unsupported, supported.
HORIZON_EXCEEDS_MODEL
422
h is above the model's maximum horizon (1,024 for hora-1.0).
Lower h, or split a long horizon into several requests; details carries model, h, max_horizon. See Models & Limits.
QUANTILE_UNSUPPORTED
422
With quantile_policy: strict, a quantile that level or point_quantile needs is not among the selected model's native quantiles. Not billed.
Use a natively available level or quantile, or drop strict; details carries model, level, unsupported, supported, plus point_quantile when that value is not native. See Prediction Intervals.
RAW_INPUT_INVALID
422
Under the raw preset the data is not a regular, finite series.
Fix what details.violations[] lists, or use the default preset. See the raw preset.
SERIES_TOO_LONG
422
An entity is longer than the selected model's maximum context. Measured per unique_id on what the model would receive: rows under raw, distinct timestamps before cleaning, the regular grid after cleaning (details.measured: rows / timestamps / model_input). An entity with a gap long enough for cleaning to keep only its latest segment (see options.gap_split_min_steps) skips the check before cleaning and is judged after it. History is never cut for you. Not billed.
Send only the most recent details.max_context points of each entity in details.series (at most 10 listed, longest first; details.series_count counts all). See Models & Limits.
SERIES_TOO_SHORT
422
After cleaning, an entity has fewer points than the model's minimum context.
Send more history (at least 16 points per entity for hora-1.0).
4. Data blocked by cleaning
All of these carry the full cleaning report in error.details.cleaning_report: blocking[] names the entity and reason, and proposed entries in decisions[] can be accepted by id. Dry-running with /v1/clean is the quickest way to investigate.
Code
HTTP
When
What to do
SERIES_DISCONTINUOUS
422
Under the default policy version 2026-13, a recent gap of 0.75 seasonal periods or more, or a missing share of 35% or more (blocking is health_critical, the check named in reason: gap_near_origin_too_long or missing_share_too_high); under the deprecated versions, gaps plus null values reaching 10% of a series; or missing values need filling but clean_series is false or the preset is strict (cleaning_required, reason: series_discontinuous); or, from policy version 2026-12 on, a series ends with more trailing null steps than can be kept (more than 3), or you declined gap.masked_tail (trailing_gap_too_long, with count and max_steps).
Fill the data; or enable clean_series / use default; under strict, accept decisions such as gap.impute by id. If blocking carries caused_by, the missing share comes from a missing-value code decision: follow its decline / accept hint. For trailing_gap_too_long, send the newest observed values or drop the trailing empty rows.
TIMESTAMP_IRREGULAR
422
Duplicate, out-of-order or off-grid stamps need repair but clean_series is false, or the strict preset does not allow the corresponding drop (reason: timestamp_irregular).
Align stamps on the client, or enable clean_series; under strict, accept by id.
FREQ_INFERENCE_FAILED
422
No stable sampling frequency can be inferred, or more than 10% of stamps are off the grid.
Pass freq explicitly.
FREQ_AMBIGUOUS
422
With freq omitted, entities infer different frequencies, or there are several candidates.
Pass freq, or send entities with different frequencies separately.
An entity has no numeric y at all, or nothing usable remains after cleaning.
Drop that entity or add observations.
AMBIGUOUS_DATE_FORMAT
422
The day/month order of D/M/YYYY-style stamps cannot be proven from the data.
Pass options.ds_format (dmy or mdy), or send ISO 8601.
5. Rate limits and server side
Code
HTTP
When
What to do
RATE_LIMIT_EXCEEDED
429
Too many requests per minute (forecast-rpm / clean-rpm), or the daily point quota does not fit (forecast-daily-points).
Wait Retry-After seconds; the daily quota frees up at the next UTC midnight. Headers and details are in Metering, Quotas & Rate Limits.
SERVICE_UNAVAILABLE
503
The cleaning or inference stage is temporarily unavailable, the selected model is unavailable, the shared cleaning capacity stayed full for the whole queue wait, or SequenxAI has paused the endpoint (the last two with Retry-After).
Retry later with exponential backoff; honour Retry-After when present.
MODEL_BUSY
503
The model is busy.
Retry after Retry-After seconds.
GATEWAY_TIMEOUT
504
Inference did not finish within the request budget.
Retry later; fewer entities or a smaller h help.
INTERNAL_SERVER_ERROR
500
An unexpected internal error.
Retry later; if it persists, contact support with the request_id.
6. Codes in the contract that are not returned today
The OpenAPI examples also show the codes below. Their features are not implemented on the public API, so they do not occur today:
Code
Why
IP_NOT_ALLOWED (403)
Keys have no IP allowlist yet.
DUPLICATE_TIMESTAMPS (422)
Only raised by a "reject duplicates" policy, which the public API does not expose; duplicate stamps keep the last row.
JOB_NOT_FOUND (404)
The batch endpoints are not open (marked x-availability: planned in the contract); requests currently get a plain 404 without the envelope, never this code.
7. Retry guidance
Retry when retryable is true: 429 (after Retry-After), 500, 503, 504, and 409 IDEMPOTENCY_IN_PROGRESS (a little later, same key). None of these is billed.
Do not retry unchanged when retryable is false: 400, 401, 403, 422. The request itself must change.
Daily-quota 429 (X-RateLimit-Limit-Type: daily) only clears at the next UTC midnight; do not wait for it in-process.
retry.py
import timeimport requestsdef retryable(resp): """error.retryable: whether the same request may succeed when sent again.""" try: return resp.json()["error"]["retryable"] is True except (ValueError, KeyError, TypeError): return Falsedef post_with_retry(url, payload, headers, attempts=4): for attempt in range(attempts): resp = requests.post(url, json=payload, headers=headers, timeout=180) if resp.ok or not retryable(resp) or attempt == attempts - 1: return resp # The daily point quota only frees up at the next UTC midnight: surface it, do not sleep. if resp.headers.get("X-RateLimit-Limit-Type") == "daily": return resp # 429 and MODEL_BUSY carry Retry-After; the other retryable errors usually do not. time.sleep(int(resp.headers.get("Retry-After", 2 ** attempt))) return resp