GET STARTED · ERRORS

Error reference — every status code the DomainScan API can return, and the right client-side reaction.

DomainScan errors pair standard HTTP status codes with stable, machine-readable codes inside a consistent error envelope. This page lists every code we can return, the conditions that produce it and the recommended retry/escalation behavior. Match on `error.code` in your client — the human-readable `error.message` is for logs and dashboards, not control flow.

01 · ENVELOPE

Error envelope shape

Every non-2xx response has the same shape. `success` is always present; `error` is always present on failure; HTTP status code is meaningful.

Error response
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "`domain` must be a valid hostname.",
    "details": {
      "field": "domain",
      "requestId": "req_01HZBN4F8K3MN7"
    }
  }
}
  • code Stable, snake-cased identifier. Safe to match in client code. Never changes for a given failure mode.
  • message Human-readable explanation. May be reworded between revisions — don't match against it.
  • details Per-error-type metadata: bad fields for VALIDATION_ERROR, retry hints for RATE_LIMITED, request ID for everything.
02 · 4XX

Client errors — your call is wrong

4xx means you sent something that can't be processed as-is. Fix the client. Retrying without changes will produce the same error.

  • 400 VALIDATION_ERROR Required parameter missing or malformed. Inspect error.details.field for the offending param. Most common: bad domain format, IP version mismatch, missing required query string.
  • 401 UNAUTHORIZED Missing or invalid bearer token. Re-authenticate. Note: anonymous calls do NOT return 401 — they fall back to per-IP rate limits, not a 401.
  • 403 FORBIDDEN Authenticated but lacks permission. Examples: free-plan key trying to access a Pro-only endpoint, key revoked but still cached locally.
  • 404 NOT_FOUND Resource doesn't exist. Domain unregistered, IP not allocated, MAC OUI not in registry. Treat as authoritative — don't retry.
  • 409 CONFLICT State conflict (e.g. attempting to create a duplicate resource). Rare on a read-only API.
  • 422 UNPROCESSABLE_ENTITY Parameters parsed but semantically wrong (e.g. start_date after end_date).
  • 429 RATE_LIMITED Per-minute, per-day or burst cap exceeded. Inspect Retry-After header. See /docs/rate-limits for backoff strategy.
03 · 5XX

Server errors — something on our side

5xx means something failed server-side. Retry with backoff. If persistent, file a ticket with the request ID.

  • 500 INTERNAL_ERROR Unhandled bug on our side. Retry once or twice; if persistent, report via /contact with error.details.requestId.
  • 502 BAD_GATEWAY Edge can't reach origin. Usually transient (deploy windows, fleet rollout). Retry with backoff.
  • 503 UPSTREAM_UNAVAILABLE An external service we depend on is down — DNS registry, blacklist DB, certificate authority, etc. error.details.upstream identifies which one. Retry with longer backoff (5–30s).
  • 504 GATEWAY_TIMEOUT Upstream timed out. Common on slow snapshots; the snapshot endpoint has an `async=true` mode that avoids this entirely.
04 · DOMAIN-SPECIFIC

Domain-specific error codes

Codes you'll see only on certain endpoints. The standard 4xx/5xx codes above still apply on top of these:

  • DOMAIN_NOT_FOUND WHOIS / RDAP returned no record. The domain is unregistered or recently dropped.
  • DOMAIN_NOT_RESOLVABLE Domain exists but DNS returns no answer for the requested record type. Common: querying AAAA on an IPv4-only domain.
  • PORT_FILTERED Port scan timed out — usually means a firewall is dropping packets (not strictly an error). Often combined with 200 OK and status=filtered in the body.
  • IP_PRIVATE Lookup attempted on a private IP range (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16). Not routable, not geolocatable.
  • TLD_NOT_SUPPORTED Some ccTLDs are not supported for WHOIS/RDAP. The list is published in /docs and updated as registries enable RDAP.
  • CERT_UNREACHABLE SSL endpoint refused connection or didn't complete TLS handshake. Often means HTTPS isn't actually configured.
05 · CLIENT PATTERN

Recommended client-side handling pattern

Four-tier handler that covers 95% of integrations:

Generalized error handler (TypeScript)
async function callApi(url: string, opts: RequestInit) {
  const res = await fetch(url, opts);
  if (res.ok) return await res.json();
  const err = await res.json().catch(() => null);
  const code = err?.error?.code || `HTTP_${res.status}`;

  switch (true) {
    case res.status === 429:                  // retry with backoff
      throw new RetryableError(code, res);
    case res.status >= 500:                   // retry with backoff
      throw new RetryableError(code, res);
    case res.status === 401 || res.status === 403:
      throw new AuthError(code);              // re-issue token / surface
    case res.status >= 400:                   // fix client; don't retry
      throw new ClientError(code, err?.error?.message);
    default:
      throw new Error(code);
  }
}