API Rate Limits
Overview
ET Ducky enforces rate limits at two layers. Transport limits are applied by the API's built-in rate limiter, per policy, to groups of endpoints. Application limits are domain-specific caps (e.g. shell-command execution) enforced in service code. Exceeding either returns HTTP 429 Too Many Requests. All limits are partitioned (per IP, per organization, or per user+org as noted) so one tenant's traffic cannot consume another tenant's allowance.
Transport rate-limit policies
| Policy | Limit | Window | Partition | Queue | Applies to |
|---|---|---|---|---|---|
fixed | 100 requests | 1 minute | global | 10 | AI / analysis endpoints (e.g. Analysis, Data Explorer) |
sliding | 200 requests | 5 minutes (5 segments) | global | 20 | Endpoints on the sliding-window policy |
discovery-per-ip | 10 requests | 1 minute | source IP | none | Anonymous agent discovery / registration-token endpoints (brute-force protection) |
oob-commands-per-org | 60 requests | 1 minute | organization | none | Out-of-band gateway REST & command endpoints |
contact-report-per-ip | 5 requests | 1 minute | source IP | none | Public contact form & status-page issue reporter |
The rejection status for all policies is 429.
Application limits
| Limit | Value | Scope |
|---|---|---|
| Shell command execution | 30 commands | per hour, per user, per organization |
| AI query quota | plan-based monthly quota | per organization (resets each billing period) |
The AI quota is a monthly quota, not a sliding rate limit — it resets per billing period rather than per time window. Exhaustion returns 429 (or 402 where a plan upgrade resolves it) with a descriptive message.
Response headers on 429
When a request is rejected for exceeding a transport limit, the API returns standard rate-limit headers so clients can back off correctly:
| Header | Meaning |
|---|---|
Retry-After | Seconds until the limited window resets. |
X-RateLimit-Limit | The rejecting policy's permit limit. |
X-RateLimit-Remaining | 0 — the request was just rejected. |
X-RateLimit-Reset | Seconds until reset (mirrors Retry-After). |
These header names are exposed via CORS so cross-origin API clients can read them. They are emitted on 429 responses; the built-in limiter does not surface a per-request remaining count on successful responses.
Client guidance
On a 429, wait for the window to reset before retrying and respect the Retry-After header. The discovery, out-of-band, and contact policies have no queue, so excess requests are rejected immediately rather than delayed. Tenants on subdomains share their organization's partitioned allowances.