Skip to content
PROBORA

Getting Started

Rate Limits

Per-plan request limits, rate-limit headers and how to back off gracefully.

Limits by plan

Limits are applied per plan. Every key inherits the limits of the plan it belongs to.

PlanRequests / minRequests / dayWebSocketHistoryPredictions
Free10500—7 days15 min delay
Pro305,000—90 daysReal time
Trader12050,0001 connection365 daysReal time
API600500,0005 connectionsFull historyReal time
EnterpriseCustomCustomCustomFull historyReal time

Prices and plan details are on the pricing page.

Rate-limit headers

HeaderDescription
X-RateLimit-LimitRequests allowed in the current window.
X-RateLimit-RemainingRequests remaining in the current window.
X-RateLimit-ResetUnix time (seconds) when the window resets.
Retry-AfterSeconds to wait before retrying (429 responses only).
Example response headers
HTTP/1.1 200 OK
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 1763150460
X-Request-Id: req_01JD3ZQ8M4F2K7W9

When you exceed a limit

Requests beyond your limit receive 429 rate_limited with a Retry-After header. Daily quotas reset at 00:00 UTC.

Response · 429
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded: 120 requests per minute.",
    "status": 429,
    "request_id": "req_01JD3ZQ8M4F2K7W9"
  }
}

Retry with backoff

Honor Retry-After on 429 responses, and back off exponentially with jitter on 5xx errors:

async function proboraGet(path, { retries = 5 } = {}) {
  for (let attempt = 0; ; attempt++) {
    const res = await fetch(`https://api.probora.xyz${path}`, {
      headers: { Authorization: `Bearer ${process.env.PROBORA_API_KEY}` },
    });
    if (res.ok) return res.json();

    const retryable = res.status === 429 || res.status >= 500;
    if (!retryable || attempt >= retries) {
      const { error } = await res.json();
      throw new Error(`${error.code} (${error.request_id})`);
    }
    const retryAfter = Number(res.headers.get("Retry-After"));
    const backoff = Math.min(30_000, 500 * 2 ** attempt) * (0.5 + Math.random() / 2);
    await new Promise((r) => setTimeout(r, retryAfter > 0 ? retryAfter * 1000 : backoff));
  }
}

Best practices

  • Predictions are frozen at window start. Fetch them once per window per market instead of polling every second. A 15-minute market changes its predictions only every 15 minutes.
  • Use GET /v1/predictions/{asset}/{timeframe} to fetch every agent for a market in one call.
  • Cache performance and leaderboard responses; they are recomputed after settlements, not continuously.
  • Spread scheduled jobs a few seconds after window boundaries rather than firing exactly on the boundary.
  • For true real-time delivery, use the WebSocket feed (coming soon).

Questions or feedback on the API? [email protected]