Skip to content

Error Codes

All API errors follow the same JSON envelope:

{
  "error": "ERROR_CODE",
  "message": "Human-readable description.",
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}

Use request_id when contacting support — it correlates to the server log entry.


Error code reference

HTTP error Description
400 INVALID_TIMEFRAME Timeframe is not one of 1m, 5m, 15m, 1h, 4h, 1d (or funding for features)
400 INVALID_TIMESTAMP start or end could not be parsed as ISO8601 or Unix ms
400 INVALID_TIME_RANGE start is after end
400 MISSING_FEATURES features parameter was empty
400 UNKNOWN_FEATURES One or more feature names are not in the catalogue
400 MISSING_SYMBOLS symbols parameter was empty (multi-asset)
400 KEY_CREATION_DISABLED Key creation requested but ADMIN_TOKEN not configured server-side
400 INVALID_TIER Unknown tier value passed to key creation
401 UNAUTHORIZED API key missing, invalid, or revoked
403 TIER_LIMIT_EXCEEDED Request requires a higher tier (timeframe, history, or symbol count)
404 QUALITY_NOT_FOUND No quality data available for the requested symbol/timeframe
404 KEY_NOT_FOUND Key hash not found when attempting to revoke
429 RATE_LIMIT_EXCEEDED Daily request quota exhausted
500 DATABASE_ERROR Unexpected internal error — retry with exponential backoff
503 SERVICE_UNAVAILABLE A backend dependency is temporarily unavailable

Rate limit response

Two independent limits apply: daily (resets at midnight UTC) and per-minute (resets each calendar minute). A 429 is returned when either is exceeded:

{
  "error": "RATE_LIMIT_EXCEEDED",
  "message": "Rate limit exceeded for free plan. Resets in 42s.",
  "retry_after_secs": 42,
  "request_id": "..."
}
HTTP/1.1 429 Too Many Requests
Retry-After: 42
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067260
X-RateLimit-Limit-Minute: 10
X-RateLimit-Remaining-Minute: 0

All successful responses also include these headers so you can track consumption proactively.


Tier limit response

403 responses for tier limits include an upgrade URL:

{
  "error": "TIER_LIMIT_EXCEEDED",
  "message": "1m timeframe requires Pro plan. Current plan: free. Upgrade at https://hypquant.com/pricing",
  "upgrade_url": "https://hypquant.com/pricing",
  "request_id": "..."
}

import time
import requests

def request_with_retry(url, headers, params, max_retries=3):
    for attempt in range(max_retries):
        resp = requests.get(url, headers=headers, params=params)
        if resp.status_code == 429:
            retry_after = int(resp.headers.get("Retry-After", 60))
            time.sleep(retry_after)
            continue
        if resp.status_code == 500:
            time.sleep(2 ** attempt)  # exponential backoff
            continue
        resp.raise_for_status()
        return resp.json()
    raise RuntimeError("Max retries exceeded")

Health check

GET /health — no authentication required.

{"status": "ok", "db": "ok", "redis": "ok"}

Returns 200 when all dependencies are healthy, 503 otherwise. Use this for load balancer health checks and uptime monitoring.