Handle errors
View as MarkdownError 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."
}
]
}| Field | Required | Description |
|---|---|---|
code | Yes | Stable, machine-readable error code. Match on this for programmatic handling. |
message | No | Human-readable message. May change between releases. |
fields[] | No | Per-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:
| Field | Required | Description |
|---|---|---|
retryAfterSec | Yes | Seconds the caller should wait before retrying the same operation. Mirrored in the Retry-After response header. |
Status codes
| Status | Meaning | Caller action |
|---|---|---|
400 Bad Request | Request was malformed (missing required field, invalid query parameter format). | Fix the request. Inspect fields[] for specifics. |
401 Unauthorized | Authorization header missing, malformed, expired, or revoked. | Re-authenticate. See Authentication. |
403 Forbidden | Authenticated 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 Found | The 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 Conflict | The 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 Entity | Request 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 Requests | Rate limit exceeded. Response is shaped as ErrorLimit. | Wait retryAfterSec seconds, then retry. Go to Handle rate limits for strategies. |
5xx Server Error | Upstream 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 checkGET /v1/domains— list your domainsGET /v1/domains/{domain}— domain detailGET /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:
| Operation | Retry-safe? |
|---|---|
POST /v1/domains/purchase | Not 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}/renew | Idempotent within a billing cycle in practice — the registry rejects duplicates — but treat the same as purchase: confirm state before retrying. |
POST /v1/domains/{domain}/transfer | Same 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
Any — applies to all API operationsRelated
Last updated on
How is this guide?