Skip to content

Rate Limiting

The ACTO API implements rate limiting to ensure fair usage and service stability.

Limits

Limit TypeValue
Default Rate5 requests/second
Burst CapacityUp to 20 requests
Per KeyRate limits apply per API key + endpoint
Bucket TTL1 hour (inactive buckets expire)

Configuration (Contributors Only)

For Contributors

These settings are for ACTO contributors running local development servers. Regular users don't need to configure rate limiting - it's handled by the hosted platform.

toml
# config.toml
rate_limit_enabled = true
rate_limit_rps = 10.0                  # Requests per second
rate_limit_burst = 50                  # Burst capacity
rate_limit_bucket_ttl = 3600.0         # Bucket expiry in seconds (1 hour)
rate_limit_cleanup_interval = 1000     # Cleanup stale buckets every N requests
Environment VariableDefaultDescription
ACTO_RATE_LIMIT_ENABLEDtrueEnable rate limiting
ACTO_RATE_LIMIT_RPS5.0Requests per second
ACTO_RATE_LIMIT_BURST20Maximum burst capacity
ACTO_RATE_LIMIT_BUCKET_TTL3600.0Bucket expiry (seconds)
ACTO_RATE_LIMIT_CLEANUP_INTERVAL1000Cleanup frequency

Rate Limit Headers

Responses include rate limit information:

http
X-RateLimit-Limit: 5
X-RateLimit-Remaining: 4
X-RateLimit-Reset: 1705320000
HeaderDescription
X-RateLimit-LimitRequests allowed per second
X-RateLimit-RemainingRemaining requests
X-RateLimit-ResetUnix timestamp when limit resets

Rate Limit Exceeded

When you exceed the rate limit, you'll receive:

http
HTTP/1.1 429 Too Many Requests
Retry-After: 2

{
  "detail": "Rate limit exceeded. Try again in 2 seconds."
}

Handling Rate Limits

Python SDK

The SDK provides a RateLimitError exception:

python
from acto.client import ACTOClient
from acto.client.exceptions import RateLimitError
import time

client = ACTOClient(api_key="...", wallet_address="...")

try:
    result = client.verify(envelope)
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after} seconds")
    time.sleep(e.retry_after or 1)
    result = client.verify(envelope)  # Retry

Exponential Backoff

For robust error handling:

python
import time
from acto.client.exceptions import RateLimitError

def verify_with_retry(client, envelope, max_retries=5):
    for attempt in range(max_retries):
        try:
            return client.verify(envelope)
        except RateLimitError as e:
            if attempt == max_retries - 1:
                raise
            
            wait = e.retry_after or (2 ** attempt)
            print(f"Rate limited. Waiting {wait}s...")
            time.sleep(wait)

JavaScript

javascript
async function verifyWithRetry(envelope, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch('https://api.actobotics.net/v1/verify', {
      method: 'POST',
      headers: {...},
      body: JSON.stringify({ envelope })
    });
    
    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After') || Math.pow(2, attempt);
      console.log(`Rate limited. Waiting ${retryAfter}s...`);
      await new Promise(r => setTimeout(r, retryAfter * 1000));
      continue;
    }
    
    return response.json();
  }
  throw new Error('Max retries exceeded');
}

Best Practices

1. Use Batch Endpoints

Instead of individual verification calls:

python
# ❌ Bad: 100 individual calls
for env in envelopes:
    client.verify(env)

# ✅ Good: 1 batch call
results = client.verify_batch(envelopes)

2. Implement Request Queuing

python
import time
from collections import deque

class RateLimitedClient:
    def __init__(self, client, rate=5):
        self.client = client
        self.rate = rate
        self.request_times = deque()
    
    def verify(self, envelope):
        self._wait_if_needed()
        return self.client.verify(envelope)
    
    def _wait_if_needed(self):
        now = time.time()
        
        # Remove requests older than 1 second
        while self.request_times and now - self.request_times[0] > 1:
            self.request_times.popleft()
        
        # Wait if at rate limit
        if len(self.request_times) >= self.rate:
            sleep_time = 1 - (now - self.request_times[0])
            if sleep_time > 0:
                time.sleep(sleep_time)
        
        self.request_times.append(time.time())

3. Cache Results

python
from functools import lru_cache

@lru_cache(maxsize=1000)
def get_proof_cached(client, proof_id):
    return client.get_proof(proof_id)

4. Monitor Usage

Track your API usage in the dashboard to stay within limits.

Increasing Limits

For higher rate limits, contact support:

Include:

  • Your use case
  • Expected request volume
  • Wallet address

https://www.actobotics.net