Skip to main content
Updated May 8, 2026 All API endpoints (except /health and /ready) require authentication via API key.

API Key Authentication

Pass your API key in the X-API-Key header. Each key is bound to a specific account.
curl -H "X-API-Key: YOUR_API_KEY" \
  https://api.rafftechnologies.com/api/v1/vms

Required Headers

HeaderRequiredDescription
X-API-KeyAlwaysYour API key for authentication
X-Project-IDMutating endpointsProject ID for create, delete, power actions, resize, and other write operations
Mutating operations (POST, DELETE, PATCH) return 400 Bad Request with "X-Project-ID required" if this header is missing.
curl -X POST https://api.rafftechnologies.com/api/v1/vms \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Project-ID: YOUR_PROJECT_ID" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-vm", "template_id": "<template-uuid>", "pricing_id": 1, "region": "us-east"}'

Response Format

API responses return the same standard fields:
{
  "id": "69590a15-...",
  "name": "ubuntu-2cpu-4gb-01",
  "status": "active",
  "cpu": 2,
  "ram": 4,
  "storage": 80,
  "public_ipv4_address": "15.204.178.3",
  "price_per_hour": "0.027764",
  "pricing_id": 3,
  "billing_type": "payg",
  "region": "us-east",
  "created_at": "2025-07-12T02:36:56Z"
}

Managing API Keys

API keys are managed from the Raff Dashboard:
  1. Click API Keys in the sidebar (it’s a top-level menu item — not under Settings)
  2. Click Create API Key
  3. Name the key, pick its Account Role (and Project Role if not Owner), set an expiration, and click Create Key
  4. Copy the key immediately — it’s only shown once
For the full create flow with roles and permissions preview, see Generate an API key.
Store your API keys securely. Never commit them to version control or expose them in client-side code.

Rate Limiting

Requests are rate-limited per API key. The tier is set on the key when it’s created (or by Raff support, on request).
TierRequests/secBurstNotes
Standard (default for all new keys)30 / sec60The right tier for typical dashboards, scripts, and automation — well above what most workloads sustain
High100 / sec200For tools that legitimately drive sustained throughput — bulk migrations, monitoring exporters scraping every endpoint, large fleet reconciliation loops
How it actually works. The limiter is a sliding 1-second window plus a separate burst budget. As long as you stay within the per-second number, you’re fine. If you exceed it briefly, the burst budget covers you for short spikes — once that’s consumed, you start getting 429s until both windows recover. Burst budget refreshes on a 60-second window. When rate-limited, the API returns:
HTTP/1.1 429 Too Many Requests
Retry-After: 1
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0

{
  "error": "Rate limit exceeded",
  "message": "Maximum 30 requests per second allowed for your tier"
}
Recommended client behavior:
  1. Honor the Retry-After header. It tells you exactly when to retry — don’t guess
  2. Implement exponential backoff for 429 and 5xx: 1s → 2s → 4s → 8s → 16s capped at ~60s
  3. Coalesce reads — if you’re polling list endpoints every 100 ms in a loop, slow down. Most resources change on second-or-slower timescales
  4. Cache catalog data/api/v1/public/templates, /api/v1/public/pricing/vm, region lists, etc. don’t change minute-to-minute. Cache them locally for the lifetime of your script

Upgrading tier

Need more than Standard sustains? Two paths:
PathWhen
Email support@rafftechnologies.comProduction migration, large fleet bulk-create, tooling that legitimately needs sustained throughput. Tell us the workload and the target peak RPS — we’ll move the key to High if it’s reasonable
Stay on StandardAnything human-driven (dashboards, ad-hoc scripts, occasional automation). Standard’s 30/sec + burst-of-60 covers it with room to spare
There’s no self-service upgrade today — it’s a support request because we want to understand the use case (and stop accidental loops from flooding the platform).

Calling the API from HTTP clients

Raff doesn’t ship a first-party SDK today. The API is plain REST + JSON, so any HTTP client works. The same headers + body apply across languages:
import os, requests

headers = {
    "X-API-Key": os.environ["RAFF_API_KEY"],
    "X-Project-ID": os.environ["RAFF_PROJECT_ID"],
    "Content-Type": "application/json",
}

resp = requests.get("https://api.rafftechnologies.com/api/v1/vms", headers=headers, timeout=30)
resp.raise_for_status()

for vm in resp.json()["data"]:
    print(vm["id"], vm["name"], vm["status"])
Last modified on May 8, 2026