Support

Handle errors

View as Markdown

Error response shape, status code semantics, and retry guidance.

Overview

The Domains API returns a consistent error envelope across all version namespaces (v1, v2, and v3). Every error response includes a stable code field for programmatic handling, and optionally field-level validation details. This page covers the response shape, status code semantics, and retry guidance.

Error response shape

Error schema (used for 4xx and 5xx responses):

{
  "code": "INVALID_BODY",
  "message": "Request body did not match the expected schema.",
  "fields": [
    {
      "path": "contactRegistrant.email",
      "code": "INVALID_FORMAT",
      "message": "Value must be a valid email address."
    }
  ]
}
FieldRequiredDescription
codeYesStable, machine-readable error code. Match on this for programmatic handling.
messageNoHuman-readable message. May change between releases.
fields[]NoPer-field validation details when the error is body- or query-scoped. Each entry has its own path (JSONPath into the request), code, and message.

ErrorLimit extends Error with one additional field for rate-limit (429) responses:

FieldRequiredDescription
retryAfterSecYesSeconds the caller should wait before retrying the same operation. Mirrored in the Retry-After response header.

Status codes

StatusMeaningCaller action
400 Bad RequestRequest was malformed (missing required field, invalid query parameter format).Fix the request. Inspect fields[] for specifics.
401 UnauthorizedAuthorization header missing, malformed, expired, or revoked.Re-authenticate. See Authentication.
403 ForbiddenAuthenticated but unauthorized — usually a missing scope or insufficient role.Issue a token with the required scope, or escalate the calling principal's authorization.
404 Not FoundThe path doesn't exist, or the addressed resource (domain, contact, record) isn't visible to this caller.Verify the path and confirm the caller owns or can access the resource.
409 ConflictThe request conflicts with current state (e.g. registering a name already taken; updating a record set that's mid-transfer).Re-read state and retry only after resolving the conflict.
422 Unprocessable EntityRequest was syntactically valid but semantically rejected (e.g. invalid registrant data, TLD-specific constraint failure, NO_PAYMENT_PROFILE).Inspect code and fields[]. For missing billing, see Set up a payment profile.
429 Too Many RequestsRate limit exceeded. Response is shaped as ErrorLimit.Wait retryAfterSec seconds, then retry. Go to Handle rate limits for strategies.
5xx Server ErrorUpstream registry or service issue.Retry idempotent operations with exponential backoff. See Retry semantics for non-idempotent ones.

Retry semantics

The following sections describe the retry semantics for read and write operations.

Read operations

All GET operations on the Domains API are safe to retry. Examples:

  • GET /v3/domains/check-availability — availability check
  • GET /v1/domains — list your domains
  • GET /v1/domains/{domain} — domain detail
  • GET /v1/domains/{domain}/records — DNS records

Write operations

Most writes can be retried after a 5xx, but the API doesn't support an Idempotency-Key request header — neither v1 nor v2 specs declare one. This affects retry strategy on a few specific operations:

OperationRetry-safe?
POST /v1/domains/purchaseNot idempotent without external coordination. A retry can result in a duplicate registration if the first attempt actually succeeded server-side. Pair every retry with a fresh GET /v1/domains/available and a state check via GET /v1/domains/{domain}.
POST /v1/domains/{domain}/renewIdempotent within a billing cycle in practice — the registry rejects duplicates — but treat the same as purchase: confirm state before retrying.
POST /v1/domains/{domain}/transferSame as purchase.
PUT /v1/domains/{domain}/records (replace)Idempotent by shape — replaying the same request produces the same record set.
PATCH /v1/domains/{domain}/records (add)Not idempotent — repeated calls add duplicate records.
DELETE /v1/domains/{domain}/records/{type}/{name}Idempotent — deleting an already-deleted record is a no-op.

Programmatic error handling

Match on code, not message. Codes are stable across releases; messages may be refined.

const res = await fetch(url, { headers });
if (!res.ok) {
  const err = await res.json();
  switch (err.code) {
    case "DOMAIN_NOT_AVAILABLE":
      // someone else registered the name first
      break;
    case "BILLING_DECLINED":
      // payment method failed — surface to user
      break;
    case "NO_PAYMENT_PROFILE":
      // no billing method on account — see payment profile setup
      break;
    default:
      // log err.code and err.fields for debugging
      throw new Error(err.code + ": " + err.message);
  }
}

Reference: the Error and ErrorLimit schemas are defined in the OpenAPI specification.

Agent & Automation Notes

ScopesAny — applies to all API operations
Rate limitN/A (informational reference)
On failureMatch the 'code' field, not HTTP status, for programmatic handling. 4xx errors are client-fixable (check input, scopes, payment profile). 5xx errors are safe to retry with exponential backoff. 429 means wait for Retry-After header.

Last updated on

How is this guide?

On this page