CTFFactory Docs

Rate Limits

CTFFactory enforces rate limits on all API requests to ensure fair usage and platform stability. Understanding these limits helps you design integrations that stay within bounds and recover gracefully when limits are reached.


Default Limit

Scope Limit
Requests per API key 120 requests per 60 seconds

The limit is applied per API key, not per workspace or IP address. Two different API keys issued to the same workspace each have their own independent 120 req/60 s bucket.

The 60-second window is a sliding window, not a fixed minute boundary. Each request is evaluated against the count of requests made in the 60 seconds preceding that request.


Rate Limit Response Headers

Every API response includes headers that let your client observe current limit consumption:

Header Description
X-RateLimit-Limit Maximum requests allowed per window (always 120)
X-RateLimit-Remaining Number of requests remaining in the current window
X-RateLimit-Reset Unix timestamp when the oldest request in the window expires

Example response headers:

HTTP/1.1 200 OK
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1747580520

HTTP 429 Too Many Requests

When the rate limit is exceeded, CTFFactory returns:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 14
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1747580520

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "You have exceeded the rate limit of 120 requests per 60 seconds. Please wait before retrying.",
    "retry_after": 14,
    "request_id": "req_01JXABCDE12345"
  }
}

The Retry-After header (and retry_after in the body) specifies the number of seconds your client should wait before sending another request. Retrying before this interval will continue to receive 429 responses.


Best Practices

Following these practices will keep your integration well within rate limits and make it resilient to transient 429 responses.

Respect Retry-After

Always read and honor the Retry-After header. Do not implement a fixed sleep interval β€” the value varies depending on how many requests are in flight.

import time
import requests

def api_get(url, headers):
    while True:
        response = requests.get(url, headers=headers)
        if response.status_code == 429:
            retry_after = int(response.headers.get("Retry-After", 10))
            print(f"Rate limited. Waiting {retry_after}s...")
            time.sleep(retry_after)
            continue
        response.raise_for_status()
        return response.json()

Monitor X-RateLimit-Remaining

Proactively slow down requests when X-RateLimit-Remaining drops below a threshold (e.g., 10) to avoid hitting the hard limit entirely.

Use Pagination Efficiently

List endpoints support per_page (up to 100 items). Fetching 100 items per page uses one request instead of four. Prefer larger page sizes when retrieving large result sets.

Cache Stable Responses

Responses such as CTF event details, challenge metadata, and scoreboards do not change every second. Cache these responses locally and refresh them at a reasonable interval (e.g., every 30 seconds for a live scoreboard display) rather than polling continuously.

Avoid Polling for Async Operations

Deployments and challenge generation are asynchronous. Instead of polling GET /ctfs/{id} in a loop, use webhooks to receive a push notification when the operation completes. This eliminates the need to make any polling requests at all.

Use Separate Keys for Independent Integrations

If you have multiple independent integrations (e.g., a CI/CD pipeline and a Slack bot), issue a dedicated API key for each. Each key has its own rate limit bucket, effectively multiplying your available throughput.


Increased Limits

If your use case legitimately requires higher throughput β€” for example, large-scale educational platforms or managed service providers running many simultaneous CTF events β€” contact CTFFactory support to discuss an increased limit arrangement. Include your workspace ID and a description of your usage pattern in the request.

πŸ‡¨πŸ‡¦ Data hosted in Canada Β· Β© 2026 ExamBoot Β· Terms Β· Privacy