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

Metering, Quotas & Rate Limits

This page covers how many points a request costs, what the free tier includes, and the 429 response you get when a limit is hit. API calls are currently metered on the free tier: usage is recorded but not charged.

1. Billable points

Only successful /v1/forecast requests are metered:

text
billable points = usage.input_data_points + usage.forecast_data_points
FieldWhat it counts
usage.input_data_pointsObservations actually fed to the model. Points dropped by a long-gap split are not counted, and neither are interpolated values or inserted timestamps.
usage.forecast_data_pointsForecast points, that is h × entities.

For example, one entity with 48 hourly observations and h = 24 costs 48 + 24 = 72 points. If 2 of those timestamps were missing and got filled, it is still counted as 46 observations: 46 + 24 = 70 points.

  • Error responses are never metered, including 4xx, 429 and 5xx.
  • /v1/clean is not metered and does not use the daily quota.
  • Client timeouts: if the request succeeded on the server, it is still metered; see the retry note in Authentication & Headers.

2. Free tier

ItemFree tier
/v1/forecast requests per minute (RPM)10
/v1/forecast points per day1,000,000 (resets at 00:00 UTC, 08:00 Beijing time)
/v1/forecast per requestAt most 200,000 rows
/v1/cleanFree, 60 calls per minute per account, at most 200,000 rows per call

The request ceiling that actually applies to your account is the X-RateLimit-Limit header on any response to any of its keys (see section 4).

3. Limits enforced today

Every limit is per account: all keys of one account share a single per-minute request window and a single daily point quota, so creating more keys does not add capacity. Different accounts do not share limits.

X-RateLimit-PolicyEndpointCountsWindow and reset
forecast-rpm/v1/forecastRequests, at most 1060-second sliding window
clean-rpm/v1/cleanRequests, at most 6060-second sliding window
forecast-daily-points/v1/forecastPoints, at most 1,000,000Per UTC calendar day, resets at UTC midnight (08:00 Beijing time)
  • Request limits are checked after authentication and before the body is parsed, so calls with a bad body still count. The rate-limited request itself is not recorded.
  • Daily points are reserved when the request starts, as "rows + h × entities"; on success the reservation is settled to the actual billable points, and on failure it is released in full. Whether a request passes therefore depends on whether its estimate fits in today's remaining quota.
  • Per-request caps are not rate limits: /v1/forecast takes at most 200,000 rows (series plus future_covariates) and /v1/clean at most 200,000; above that you get 422 POINTS_EXCEEDED right away, before any cleaning or quota use. The cap follows the memory of the cleaning host, so one oversized request is refused rather than queued; split it into several requests.
  • Per-series context is not a rate limit either: an entity longer than the selected model's maximum context returns 422 SERIES_TOO_LONG and is not billed. History is never cut for you; see Models & Limits.
  • Cleaning capacity is shared by all accounts and is not a rate limit either: only so much data is cleaned at once. While it is full, /v1/forecast queues briefly; /v1/clean does not queue and returns 503 SERVICE_UNAVAILABLE with Retry-After (the call still counts against clean-rpm). Retry after the indicated interval.

4. The 429 response

When a request limit is exceeded:

429-rpm.http
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1790665223
X-RateLimit-Limit-Type: rpm
X-RateLimit-Policy: clean-rpm
X-Request-Id: req_7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "category": "rate_limited",
    "retryable": true,
    "message": "Too many requests for this account; please respect the Retry-After interval",
    "request_id": "req_7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d",
    "details": {"limit_type": "rpm", "limit_scope": "account", "limit_value": 60, "retry_after_seconds": 23}
  }
}
HeaderRequest limit (rpm)Daily points (daily)
Retry-AfterSeconds until the window has roomSeconds until the next UTC midnight
X-RateLimit-LimitRequests per minutePoints per day
X-RateLimit-Remaining0Points left today
X-RateLimit-ResetUnix time (seconds) when the window has roomUnix time (seconds) of the next UTC midnight
X-RateLimit-Limit-Typerpmdaily
X-RateLimit-Policyforecast-rpm or clean-rpmforecast-daily-points

The X-RateLimit-* headers are not limited to 429. Once the key is authenticated, every /v1/forecast and /v1/clean response carries them, successes and other errors included, describing the request limit (rpm) of the key's account after the current request: X-RateLimit-Remaining is how many more requests fit in the current 60-second window, so you can slow down before being refused. Requests made with the account's other keys count down the same window. Only a 429 reports the bucket that was exhausted, which may be daily. Responses to a rejected key (401) carry none of them.

On a 429, details.limit_scope is always account: the exhausted limit belongs to the account, so retrying with another key of the same account does not get through.

When the daily quota does not fit, details also carries the reason and the request's estimate:

429-daily.json
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "category": "rate_limited",
    "retryable": true,
    "message": "Daily point quota of this account exhausted",
    "request_id": "req_2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e",
    "details": {
      "limit_type": "daily",
      "limit_scope": "account",
      "limit_value": 1000000,
      "retry_after_seconds": 30512,
      "reason": "daily_quota",
      "requested_points": 2400
    }
  }
}

If requested_points is larger than X-RateLimit-Remaining, a smaller request (fewer entities or shorter history) can still fit in today's remaining quota.

5. Client retries

Request limits suit an automatic retry after Retry-After; the daily quota only frees up at UTC midnight, so do not wait for it in-process.

retry.py
import time

import requests


def 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 False


def 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