API Reference
Rate Limits

Rate Limits

The oec.sh API uses per-key rate limiting to ensure fair usage and platform stability.


Limits by Key Type

Key TypeLimitWindow
Read-only (oec_live_ro_*)120 requests60 seconds
Full access (oec_live_rw_*)20 requests60 seconds
Webhook mutations (create/update/delete)10 requests60 seconds

The webhook mutation limit applies in addition to the key-level limit. A full-access key can make 20 total requests per minute, of which at most 10 can be webhook write operations.


Rate Limit Headers

Every API response includes headers showing your current limit status:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the window
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-WindowWindow duration in seconds (always 60)

Example response headers:

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 87
X-RateLimit-Window: 60

When You Hit the Limit

When you exceed the rate limit, the API returns:

HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 20
X-RateLimit-Remaining: 0
X-RateLimit-Window: 60
{
  "error": "rate_limit_exceeded",
  "message": "Rate limit exceeded. You have made 20 requests in the last 60 seconds. Please wait before retrying.",
  "retry_after": 23
}

The Retry-After header and retry_after field tell you how many seconds to wait before your next request will succeed.


Best Practices

Check Headers Before Hitting 429

Read X-RateLimit-Remaining on every response. If it drops to 0 or 1, pause before the next request:

import requests
import time
 
def api_request(session, url):
    response = session.get(url)
 
    remaining = int(response.headers.get("X-RateLimit-Remaining", 1))
    if remaining <= 1:
        # Wait for the window to reset before sending more requests
        time.sleep(5)
 
    return response.json()

Use Exponential Backoff on 429

If you receive a 429, wait for the Retry-After seconds, then retry with exponential backoff:

import requests
import time
 
def request_with_backoff(session, url, max_retries=3):
    for attempt in range(max_retries):
        response = session.get(url)
 
        if response.status_code == 429:
            retry_after = int(response.headers.get("Retry-After", 5))
            wait = retry_after * (2 ** attempt)  # exponential backoff
            print(f"Rate limited. Waiting {wait}s before retry {attempt + 1}/{max_retries}")
            time.sleep(wait)
            continue
 
        return response.json()
 
    raise Exception("Max retries exceeded")

Reduce Request Volume

  • Cache responses — environment status doesn't need to be fetched more than once every 30 seconds
  • Use webhooks instead of polling — subscribe to environment.status_changed events instead of polling /environments/{id} in a loop
  • Batch where possible — listing endpoints return up to 100 items per page; prefer one list call over many individual GET /environments/{id} calls

Webhooks are the right tool for status monitoring. Instead of polling the API every few seconds to check if a deployment finished, subscribe to the deployment.completed and deployment.failed webhook events — you'll be notified instantly without any polling overhead.


Increasing Your Limit

Rate limits are set per key type and are not currently configurable. If you have a legitimate use case that requires higher limits (e.g., a large agency managing 50+ environments with automated tooling), contact support at support@oec.sh to discuss options.