Skip to main content
The HopX API implements rate limiting to ensure fair usage and system stability. Rate limits vary by endpoint and account plan.

Rate Limit Tiers

Default Limits

Endpoint TypeLimitWindow
Control Plane API100 requestsper minute
VM Agent API300 requestsper minute
Template Builds10 buildsper hour
Sandbox Creation20 sandboxesper minute

Plan-Based Limits

Rate limits may vary based on your account plan:
  • Free Tier - Default limits
  • Pro Tier - 2x default limits
  • Enterprise - Custom limits

Rate Limit Headers

Every API response includes rate limit headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1706400060
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the window
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when the limit resets

Rate Limit Exceeded

When you exceed the rate limit, you’ll receive a 429 Too Many Requests response:
{
  "error": "Rate limit exceeded: 100 requests per minute",
  "code": "RATE_LIMIT_EXCEEDED",
  "request_id": "req_abc123",
  "retry_after": 30
}
The retry_after field indicates how many seconds to wait before retrying.

Best Practices

1. Monitor Rate Limit Headers

import requests

response = requests.get(
    "https://api.hopx.dev/v1/sandboxes",
    headers={"Authorization": "Bearer $HOPX_API_KEY"}
)

remaining = int(response.headers.get('X-RateLimit-Remaining', 0))
limit = int(response.headers.get('X-RateLimit-Limit', 0))

print(f"Rate limit: {remaining}/{limit}")

if remaining < 10:
    print("Warning: Approaching rate limit")

2. Implement Exponential Backoff

import time
import requests

def make_request_with_backoff(url, headers, max_retries=5):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)
        
        if response.status_code != 429:
            return response
        
        # Rate limited - wait and retry
        retry_after = int(response.headers.get('Retry-After', 2 ** attempt))
        print(f"Rate limited. Retrying after {retry_after}s")
        time.sleep(retry_after)
    
    raise Exception("Max retries exceeded")

3. Batch Operations

Instead of making many individual requests, batch operations when possible:
# Bad: One request per sandbox
for sandbox_id in sandbox_ids:
    response = requests.get(
        f"https://api.hopx.dev/v1/sandboxes/{sandbox_id}",
        headers=headers
    )
    process(response.json())

# Good: One request for all sandboxes
response = requests.get(
    "https://api.hopx.dev/v1/sandboxes",
    headers=headers
)
sandboxes = response.json()["data"]
for sandbox in sandboxes:
    process(sandbox)

4. Cache Responses

Cache responses that don’t change frequently:
import time
from functools import lru_cache

@lru_cache(maxsize=100)
def get_template(template_id, cache_time):
    response = requests.get(
        f"https://api.hopx.dev/v1/templates/{template_id}",
        headers={"Authorization": "Bearer $HOPX_API_KEY"}
    )
    return response.json()

# Cache for 5 minutes
cache_key = int(time.time() // 300)
template = get_template("code-interpreter", cache_key)

5. Distribute Requests Over Time

import time

sandboxes_to_create = 50
rate_limit_per_minute = 20
delay = 60 / rate_limit_per_minute  # 3 seconds

for i in range(sandboxes_to_create):
    create_sandbox()
    if i < sandboxes_to_create - 1:
        time.sleep(delay)

Rate Limit Strategies

Sliding Window

Rate limits use a sliding window algorithm, not a fixed window. This means:
  • Limits are enforced per 60-second rolling window
  • Request counts decay as time passes
  • Burst traffic is allowed up to the limit

Per-Endpoint Limits

Some endpoints have separate rate limits:
EndpointLimitReason
/v1/templates/build10/hourResource-intensive operation
/v1/sandboxes (POST)20/minutePrevent abuse
/v1/sandboxes (GET)100/minuteStandard limit

Handling Rate Limits in Production

Production-Ready Handler

import time
import requests
from typing import Optional

class RateLimitedClient:
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://api.hopx.dev"
        self.headers = {"Authorization": f"Bearer {api_key}"}
    
    def request(self, method: str, path: str, **kwargs) -> requests.Response:
        max_retries = 5
        
        for attempt in range(max_retries):
            response = requests.request(
                method,
                f"{self.base_url}{path}",
                headers=self.headers,
                **kwargs
            )
            
            # Success
            if response.status_code < 400:
                return response
            
            # Rate limited
            if response.status_code == 429:
                retry_after = int(response.headers.get('Retry-After', 2 ** attempt))
                print(f"Rate limited. Waiting {retry_after}s (attempt {attempt + 1}/{max_retries})")
                time.sleep(retry_after)
                continue
            
            # Other errors - don't retry
            response.raise_for_status()
        
        raise Exception("Max retries exceeded due to rate limiting")
    
    def get(self, path: str, **kwargs) -> requests.Response:
        return self.request("GET", path, **kwargs)
    
    def post(self, path: str, **kwargs) -> requests.Response:
        return self.request("POST", path, **kwargs)

# Usage
client = RateLimitedClient("$HOPX_API_KEY")
response = client.get("/v1/sandboxes")
sandboxes = response.json()

Increasing Rate Limits

If you need higher rate limits:
  1. Upgrade Your Plan - Higher tiers include increased limits
  2. Contact Support - Request custom limits for your use case
  3. Optimize Your Code - Reduce unnecessary API calls

Monitoring Usage

Check your current usage via the API:
curl https://api.hopx.dev/v1/account/usage \
  -H "Authorization: Bearer $HOPX_API_KEY"
Response:
{
  "rate_limits": {
    "control_plane": {
      "limit": 100,
      "remaining": 85,
      "reset_at": "2025-01-28T00:01:00Z"
    },
    "vm_agent": {
      "limit": 300,
      "remaining": 250,
      "reset_at": "2025-01-28T00:01:00Z"
    }
  }
}

Next Steps