# Handle errors (https://developer.godaddy.com/en/docs/api-users/errors)



## 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):

```json
{
  "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](https://developer.godaddy.com/docs/api-users/auth).                                                           |
| `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](https://developer.godaddy.com/docs/api-users/payment-profile).   |
| `429 Too Many Requests`    | Rate limit exceeded. Response is shaped as `ErrorLimit`.                                                                                         | Wait `retryAfterSec` seconds, then retry. Go to [Handle rate limits](https://developer.godaddy.com/docs/api-users/rate-limits) for strategies.      |
| `5xx Server Error`         | Upstream registry or service issue.                                                                                                              | Retry idempotent operations with exponential backoff. See [Retry semantics](#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:

| 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.

```js
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.
