Skip to main content

Documentation Index

Fetch the complete documentation index at: https://vmarea.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

The VMArea Public API uses two stacked buckets, both backed by Redis so the limits are shared across every API instance.

Current limits

BucketLimitKeyed byApplies to
Public API (per token)600 requests / minute (10 RPS sustained)API token IDEvery /api/public/v1/* request after x-api-key auth succeeds
Global fallback (per IP)100 requests / minuteIP addressAll traffic — primarily catches abuse of unauthenticated routes (login, signup)
The per-token bucket means heavy customers and clients behind shared NAT IPs aren’t penalised for each other’s traffic. Pair this with the Idempotency-Key header so safe retries don’t burn extra requests — see Idempotency.

429 response

When you exceed either limit, the API returns HTTP 429 Too Many Requests:
{
  "success": false,
  "error": "Rate limit exceeded for this API token: 600 requests per minute. Retry after 42."
}
The error string identifies which bucket you tripped (per-token vs per-IP global fallback).

Response headers

Every response includes the standard rate-limit headers; Retry-After is added on 429 responses.
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window.
X-RateLimit-RemainingRequests remaining in the current window.
X-RateLimit-ResetSeconds until the window resets.
Retry-AfterSeconds to wait before retrying (only on 429).

Handling 429 in your client

Read Retry-After for the minimum wait, then back off exponentially with jitter starting at 1 second:
async function requestWithRetry(url, options, maxAttempts = 5) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    const res = await fetch(url, options);
    if (res.status !== 429) return res;

    const retryAfter = parseInt(res.headers.get("Retry-After") ?? "1", 10);
    const base = Math.max(retryAfter, 1) * 1000;
    const jitter = Math.random() * 1000;
    const delay = base * Math.pow(2, attempt - 1) + jitter;
    await new Promise((r) => setTimeout(r, delay));
  }
  throw new Error("Rate limit: max retry attempts exceeded");
}

Best practices

  • Use Idempotency-Key for writes. Safe retries won’t double-spend your quota or create duplicate resources.
  • Batch reads where possible. Paginated list endpoints are cheaper than fan-out GETs.
  • Cache catalog data. Plans, regions, and OS templates change infrequently — cache them locally rather than re-fetching.
  • Use webhooks for state changes. Subscribing to vm.started, vm.stopped, etc. avoids polling GET /vms/:id in a tight loop.
  • Need higher limits? Contact support — sustained 10 RPS covers virtually every integration, but we can raise per-token ceilings on request.