Skip to main content

Understand and handle API errors

The CaptainDNS public API returns every error in a standardized JSON envelope. This page lists the 10 canonical codes, what they mean and the recommended corrective action.

Standard envelope

Every error response follows this format:

{
  "code": "QUOTA_EXCEEDED",
  "message": "Monthly credits quota reached for the current plan",
  "details": {
    "tier": "starter",
    "limit": 50000,
    "current": 50000
  },
  "request_id": "req_a1b2c3d4"
}
  • code: stable string documented here. Use it to branch your error handling logic.
  • message: human-readable description in English. Do not parse it; it may evolve.
  • details: optional object with context specific to the code. Its schema varies by code.
  • request_id: CaptainDNS request identifier, required when opening a support ticket.

The HTTP status always accompanies the code. A robust client branches on the (status, code) pair and uses code as the primary key.

Authentication codes (401)

INVALID_API_KEY

Status: 401.

Cause: missing Authorization header, missing prefix, malformed key, or tampered secret.

Action: verify the header is Authorization: Bearer cdns_live_.... A stray whitespace, a newline, or a truncated key are the most common culprits.

EXPIRED_API_KEY

Status: 401.

Cause: the key has passed its expires_at date.

Action: create a new key with a later expiration or no expiration. Keys without expires_at are valid until revoked.

REVOKED_API_KEY

Status: 401.

Cause: the key was revoked, either manually or automatically (leak detection, rotation grace period end).

Action: use the active key. If you do not have one, create one from the dashboard.

Authorization codes (403)

INSUFFICIENT_SCOPE

Status: 403.

Cause: the key lacks the scope required by the endpoint.

{
  "code": "INSUFFICIENT_SCOPE",
  "message": "Endpoint requires scope 'web:read'",
  "details": {
    "required": "web:read",
    "granted": ["dns:read", "mail:read"]
  }
}

Action: create a new key with the missing scope or use a key that carries it. Scopes are not editable after creation.

IP_NOT_ALLOWED

Status: 403.

Cause: the key has an IP allowlist and the request's source IP is not in it.

{
  "code": "IP_NOT_ALLOWED",
  "message": "Source IP not in the allowlist for this API key",
  "details": {
    "detected_ip": "203.0.113.42",
    "allowlist": ["4.175.114.0/23"]
  }
}

Action: add your IP to the key allowlist, or route your traffic through an authorized egress (proxy, NAT). Also verify your reverse proxy forwards X-Forwarded-For correctly.

QUOTA_EXCEEDED

Status: 403.

Cause: the monthly credits quota is reached and the plan does not allow overage (Free plan, or paid plan with a manually-enforced hard cap).

{
  "code": "QUOTA_EXCEEDED",
  "message": "Monthly credits quota reached",
  "details": {
    "tier": "free",
    "limit": 500,
    "current": 500,
    "period_start": "2026-04-01T00:00:00Z",
    "period_end": "2026-05-01T00:00:00Z"
  }
}

Action: wait until the next period or upgrade to a higher plan. The /account/billing dashboard lets you switch plans in one click.

Conflict codes (409)

IDEMPOTENCY_CONFLICT

Status: 409.

Cause: an Idempotency-Key is reused with a body different from the original request.

{
  "code": "IDEMPOTENCY_CONFLICT",
  "message": "Idempotency key reused with a different request body",
  "details": {
    "key": "550e8400-e29b-41d4-a716-446655440000"
  }
}

Action: generate a new key for the new request, or fix the client that mutates the body between retries.

Rate limit codes (429)

RATE_LIMITED

Status: 429.

Cause: the per-key token bucket is empty or the per-IP rate limit is exceeded.

{
  "code": "RATE_LIMITED",
  "message": "Rate limit exceeded",
  "details": {
    "scope": "per-key",
    "retry_after_seconds": 12
  }
}

The Retry-After header always accompanies this code. Wait the indicated duration before retrying. See the rate limiting guide for backoff strategies.

Server codes (5xx)

INTERNAL_ERROR

Status: 500.

Cause: unexpected error on the CaptainDNS side.

Action: keep the request_id and open a support ticket. Retry in a few minutes: some of these errors are transient (backend dependency restarting, fleeting bug). If the rate exceeds 1 %, contact the CaptainDNS team.

SERVICE_UNAVAILABLE

Status: 503.

Cause: the subsystem required by the endpoint is unavailable (for example, authentication pepper not loaded, DB in maintenance).

{
  "code": "SERVICE_UNAVAILABLE",
  "message": "Public API is temporarily unavailable",
  "details": {
    "reason": "temporarily_unavailable"
  }
}

Action: respect the Retry-After header when present. If the error persists past a few minutes, contact support. Incidents are announced on the official status channel.

Validation codes (400)

The API also returns 400 BAD_REQUEST for validation errors specific to each endpoint. These errors are not covered by the standardized envelope above because their structure varies per endpoint (each handler returns its own schema).

Example for a DNS resolve with an invalid domain:

{
  "error": "invalid domain: must be a valid FQDN",
  "field": "domain"
}

For endpoints that return validation errors, see the OpenAPI reference which documents the exact schemas.

Synthetic table

StatusCodeDescriptionAction
400VariesValidation errorFix the request body
401INVALID_API_KEYMissing or malformed keyCheck the Authorization header
401EXPIRED_API_KEYExpired keyCreate a new key
401REVOKED_API_KEYRevoked keyUse an active key
403INSUFFICIENT_SCOPEMissing scope on keyCreate a key with the right scope
403IP_NOT_ALLOWEDIP not in allowlistAdd IP or use an authorized egress
403QUOTA_EXCEEDEDMonthly quota reachedWait or upgrade plan
409IDEMPOTENCY_CONFLICTSame key with different bodyGenerate a new key
429RATE_LIMITEDRate limit exceededWait Retry-After
500INTERNAL_ERRORServer errorRetry and open a ticket if persistent
503SERVICE_UNAVAILABLEDependency unavailableWait, check status

Error handling best practices

  • Branch on the (status, code) pair. Status alone is not enough: two 403s can come from INSUFFICIENT_SCOPE or IP_NOT_ALLOWED, each deserving a different action.
  • Log the request_id. It is the unique identifier on the CaptainDNS side and is required for any investigation with support.
  • Do not retry blindly. 4xx codes other than 429 indicate a client-side problem: retrying without change will not succeed.
  • Respect Retry-After. 429 and 503 responses always include it. Do not bypass.
  • Alert on error rates. 1 % of 5xx over a 5-minute window is an incident signal. Surface it in your observability stack.

Jump to the OpenAPI reference to explore schemas endpoint by endpoint, or go back to the quickstart to restart from the beginning.