# Register a domain (https://developer.godaddy.com/en/docs/api-users/purchase-domains/register)



## Overview

This guide covers registering a domain programmatically using the GoDaddy Domains API.

Registration charges the account's billing method and isn't reversible. Make sure you have a [payment method on file](https://developer.godaddy.com/docs/api-users/payment-profile) before registering.

<Callout type="warning" title="WHOIS accuracy">
  ICANN requires that registrant contact information be accurate. Providing false contact information can result in domain suspension. Use WHOIS privacy after registration if you want to protect your contact details. Don't enter fake data.
</Callout>

## Prerequisites

* a GoDaddy account with a payment method on file
  <Callout type="info" title="Note">
    Go to [Create account](https://sso.godaddy.com/account/create/) to create a GoDaddy account and add or update payment methods.
  </Callout>
* a [PAT](https://developer.godaddy.com/docs/api-users/auth) with `domains.domain:create` scope
* a registrant contact with name, email, phone, and mailing address
* a confirmed available domain
* *(optional)* the [GoDaddy CLI](https://developer.godaddy.com/docs/api-users/cli-setup) installed and configured

## Register a domain

v3 separates pricing from execution. You get a price quote first, locking a `quoteToken`, then execute the registration with that token. This ensures the price you agreed to is the price charged. Always poll at least once even if the initial response appears synchronous.

<Callout type="info" title="Note">
  v3 does not accept inline payment or registrant contact overrides. The billing method and registrant contact are resolved from the account profile. If the account is missing a payment method or required contact fields, the quote returns `422` with field-level details.
</Callout>

### Check availability

Confirm the domain is available before quoting.

* Run the availability check:

  <Tabs items={['curl', 'Node', 'Python', 'Go']}>
    <>
      <Tab value="curl">
        ```bash
        curl -s "https://api.godaddy.com/v3/domains/check-availability?domain=example.com" \
          -H "Authorization: Bearer $GODADDY_PAT"
        ```
      </Tab>

      <Tab value="Node">
        ```js
        const res = await fetch(
          "https://api.godaddy.com/v3/domains/check-availability?domain=example.com",
          { headers: { Authorization: `Bearer ${process.env.GODADDY_PAT}` } }
        );
        const { available } = await res.json();
        if (!available) throw new Error("Domain not available");
        ```
      </Tab>

      <Tab value="Python">
        ```python
        import os, requests

        res = requests.get(
            "https://api.godaddy.com/v3/domains/check-availability",
            params={"domain": "example.com"},
            headers={"Authorization": f"Bearer {os.environ['GODADDY_PAT']}"},
        )
        data = res.json()
        assert data["available"], "Domain not available"
        ```
      </Tab>

      <Tab value="Go">
        ```go
        package main

        import (
            "encoding/json"
            "fmt"
            "net/http"
            "os"
        )

        func main() {
            req, _ := http.NewRequest("GET",
                "https://api.godaddy.com/v3/domains/check-availability?domain=example.com", nil)
            req.Header.Set("Authorization", "Bearer "+os.Getenv("GODADDY_PAT"))
            res, _ := http.DefaultClient.Do(req)
            defer res.Body.Close()

            var data struct{ Available bool `json:"available"` }
            json.NewDecoder(res.Body).Decode(&data)
            if !data.Available { panic("not available") }
            fmt.Println("available")
        }
        ```
      </Tab>
    </>
  </Tabs>

<Callout type="info" title="Note">
  Go to [Search availability](https://developer.godaddy.com/docs/api-users/search-domains) for bulk checks and suggestions.
</Callout>

### Get a registration quote

`POST /v3/domains/registration-quotes` locks the price and returns the `requiredAgreements` list you need for the next step. The `quoteToken` guarantees the price you see is the price charged at registration.

* Get a quote:

  <Tabs items={['curl', 'Node', 'Python', 'Go']}>
    <>
      <Tab value="curl">
        ```bash
        curl -s -X POST "https://api.godaddy.com/v3/domains/registration-quotes" \
          -H "Authorization: Bearer $GODADDY_PAT" \
          -H "Content-Type: application/json" \
          -d '{"domain": "example.com", "period": 1}'
        ```
      </Tab>

      <Tab value="Node">
        ```js
        const res = await fetch("https://api.godaddy.com/v3/domains/registration-quotes", {
          method: "POST",
          headers: {
            Authorization: `Bearer ${process.env.GODADDY_PAT}`,
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ domain: "example.com", period: 1 }),
        });
        const { quoteToken, expiresAt, requiredAgreements } = await res.json();
        console.log("Quote locked until", expiresAt);
        ```
      </Tab>

      <Tab value="Python">
        ```python
        import os, requests

        res = requests.post(
            "https://api.godaddy.com/v3/domains/registration-quotes",
            json={"domain": "example.com", "period": 1},
            headers={
                "Authorization": f"Bearer {os.environ['GODADDY_PAT']}",
                "Content-Type": "application/json",
            },
        )
        res.raise_for_status()
        quote = res.json()
        required_agreements = quote["requiredAgreements"]
        print("Quote locked until", quote["expiresAt"])
        ```
      </Tab>

      <Tab value="Go">
        ```go
        package main

        import (
            "bytes"
            "encoding/json"
            "fmt"
            "net/http"
            "os"
        )

        func main() {
            body := bytes.NewReader([]byte(`{"domain": "example.com", "period": 1}`))
            req, _ := http.NewRequest("POST",
                "https://api.godaddy.com/v3/domains/registration-quotes", body)
            req.Header.Set("Authorization", "Bearer "+os.Getenv("GODADDY_PAT"))
            req.Header.Set("Content-Type", "application/json")

            res, _ := http.DefaultClient.Do(req)
            defer res.Body.Close()

            var quote struct {
                QuoteToken string `json:"quoteToken"`
                ExpiresAt  string `json:"expiresAt"`
                RequiredAgreements []struct {
                    AgreementType string `json:"agreementType"`
                    Title         string `json:"title"`
                    URL           string `json:"url"`
                } `json:"requiredAgreements"`
            }
            json.NewDecoder(res.Body).Decode(&quote)
            fmt.Println("Quote locked until", quote.ExpiresAt)
        }
        ```
      </Tab>
    </>
  </Tabs>

  ```json
  {
    "quoteToken": "qt_abc123...",
    "expiresAt": "2026-01-15T10:45:00.000Z",
    "requiredAgreements": [
      {
        "agreementType": "API_DPA",
        "title": "API Domain Purchase Agreement",
        "url": "https://www.godaddy.com/agreements/showdoc?pageid=reg_sa"
      }
    ],
    "items": [
      {
        "domain": "example.com",
        "period": 1,
        "price": 1199990,
        "currency": "USD"
      }
    ]
  }
  ```

<Callout type="info" title="Note">
  The `quoteToken` expires after a short window. If it expires before you execute, fetch a new quote.
</Callout>

### Review the required agreements

The quote response includes a `requiredAgreements` list (the legal agreements you need to review before you submit the registration). Each item has a `title` for display and a `url` linking to the full legal text.

1. Display each agreement to the user:

   <Tabs items={['Node', 'Python', 'Go']}>
     <>
       <Tab value="Node">
         ```js
         for (const agreement of requiredAgreements) {
           console.log(`Review: ${agreement.title}`);
           console.log(`Full text: ${agreement.url}`);
         }
         ```
       </Tab>

       <Tab value="Python">
         ```python
         for agreement in required_agreements:
             print(f"Review: {agreement['title']}")
             print(f"Full text: {agreement['url']}")
         ```
       </Tab>

       <Tab value="Go">
         ```go
         for _, agreement := range quote.RequiredAgreements {
             fmt.Println("Review:", agreement.Title)
             fmt.Println("Full text:", agreement.URL)
         }
         ```
       </Tab>
     </>
   </Tabs>

2. After the user confirms, capture the agreement types and consent timestamp:

   <Tabs items={['Node', 'Python', 'Go']}>
     <>
       <Tab value="Node">
         ```js
         const agreedAt = new Date().toISOString();
         const agreedTypes = requiredAgreements.map((a) => a.agreementType);
         ```
       </Tab>

       <Tab value="Python">
         ```python
         from datetime import datetime, timezone
         agreed_at = datetime.now(timezone.utc).isoformat()
         agreed_types = [a["agreementType"] for a in required_agreements]
         ```
       </Tab>

       <Tab value="Go">
         ```go
         agreedAt := time.Now().UTC().Format(time.RFC3339)
         agreedTypes := make([]string, len(quote.RequiredAgreements))
         for i, a := range quote.RequiredAgreements {
             agreedTypes[i] = a.AgreementType
         }
         ```
       </Tab>
     </>
   </Tabs>

<Callout type="warning" title="ICANN compliance">
  Use the actual timestamp of when the user clicked accept for `agreedAt` — not the time of the API call. Synthetic timestamps violate ICANN policy.
</Callout>

<Callout type="info" title="Agreements vary by TLD">
  Required agreements depend on the TLD. `.ca` domains require a CIRA agreement, `.au` domains require an AURA agreement. Always use the `agreementType` values from `requiredAgreements` — don't hardcode them.
</Callout>

### Submit the registration

Send the `quoteToken` and consent to execute the registration. Always include an `Idempotency-Key` header — if the request times out or returns `5xx`, replay with the same key and the server deduplicates.

<Callout type="warning" title="Charges to your account">
  `POST /v3/domains/registrations` charges the account's billing method. Verify the domain name and the quoted price before executing.
</Callout>

* Submit the registration request:

  <Tabs items={['curl', 'Node', 'Python', 'Go']}>
    <>
      <Tab value="curl">
        ```bash
        curl -s -X POST "https://api.godaddy.com/v3/domains/registrations" \
          -H "Authorization: Bearer $GODADDY_PAT" \
          -H "Content-Type: application/json" \
          -H "Idempotency-Key: $(uuidgen)" \
          -d '{
            "quoteToken": "qt_abc123...",
            "domain": "example.com",
            "period": 1,
            "consent": {
              "agreedAt": "2026-01-15T10:30:00.000Z",
              "agreementTypes": ["API_DPA"]
            }
          }'
        ```
      </Tab>

      <Tab value="Node">
        ```js
        import { randomUUID } from "node:crypto";

        const res = await fetch("https://api.godaddy.com/v3/domains/registrations", {
          method: "POST",
          headers: {
            Authorization: `Bearer ${process.env.GODADDY_PAT}`,
            "Content-Type": "application/json",
            "Idempotency-Key": randomUUID(),
          },
          body: JSON.stringify({
            quoteToken: "qt_abc123...",
            domain: "example.com",
            period: 1,
            consent: {
              agreedAt,                    // timestamp captured in step 3
              agreementTypes: agreedTypes,  // types from requiredAgreements in step 3
            },
          }),
        });
        if (!res.ok) throw new Error(`Registration failed: ${res.status}`);
        const { registrationId, domain } = await res.json();
        console.log("Registered:", domain, "registrationId:", registrationId);
        ```
      </Tab>

      <Tab value="Python">
        ```python
        import os, uuid, requests

        res = requests.post(
            "https://api.godaddy.com/v3/domains/registrations",
            json={
                "quoteToken": "qt_abc123...",
                "domain": "example.com",
                "period": 1,
                "consent": {
                    "agreedAt": agreed_at,        # timestamp captured in step 3
                    "agreementTypes": agreed_types,  # types from requiredAgreements in step 3
                },
            },
            headers={
                "Authorization": f"Bearer {os.environ['GODADDY_PAT']}",
                "Content-Type": "application/json",
                "Idempotency-Key": str(uuid.uuid4()),
            },
        )
        res.raise_for_status()
        data = res.json()
        print("Registered:", data["domain"], "registrationId:", data["registrationId"])
        ```
      </Tab>

      <Tab value="Go">
        ```go
        package main

        import (
            "bytes"
            "encoding/json"
            "fmt"
            "net/http"
            "os"
            "time"

            "github.com/google/uuid"
        )

        func main() {
            body := map[string]any{
                "quoteToken": "qt_abc123...",
                "domain":     "example.com",
                "period":     1,
                "consent": map[string]any{
                    "agreedAt":       agreedAt,    // timestamp captured in step 3
                    "agreementTypes": agreedTypes,  // types from requiredAgreements in step 3
                },
            }
            b, _ := json.Marshal(body)
            req, _ := http.NewRequest("POST",
                "https://api.godaddy.com/v3/domains/registrations", bytes.NewReader(b))
            req.Header.Set("Authorization", "Bearer "+os.Getenv("GODADDY_PAT"))
            req.Header.Set("Content-Type", "application/json")
            req.Header.Set("Idempotency-Key", uuid.NewString())

            res, _ := http.DefaultClient.Do(req)
            defer res.Body.Close()
            if res.StatusCode != 202 { panic(fmt.Sprint("registration failed: ", res.StatusCode)) }

            var reg struct {
                RegistrationID string `json:"registrationId"`
                Domain         string `json:"domain"`
            }
            json.NewDecoder(res.Body).Decode(&reg)
            fmt.Println("Registered:", reg.Domain, "registrationId:", reg.RegistrationID)
        }
        ```
      </Tab>
    </>
  </Tabs>

<Callout type="warning" title="Consent and idempotency requirements">
  Use the actual consent timestamp for `agreedAt`; synthetic values violate ICANN policy. Use the `agreementTypes` keys from `requiredAgreements` in the quote response; the registration returns `INVALID_AGREEMENT_KEYS` if they don't match. The `period` must match the quoted period, or you get `QUOTE_MISMATCH`. The `Idempotency-Key` must be a unique UUID per attempt; retrying with the same key is safe after a timeout.
</Callout>

### Poll the operation

The registration is asynchronous. Poll the returned `registrationId` URL every few seconds until the status reaches a terminal state.

* Poll for the registration status:

  <Tabs items={['curl', 'Node', 'Python', 'Go']}>
    <>
      <Tab value="curl">
        ```bash
        curl -s "https://api.godaddy.com/v3/domains/registrations/{registrationId}" \
          -H "Authorization: Bearer $GODADDY_PAT"
        ```
      </Tab>

      <Tab value="Node">
        ```js
        async function pollRegistration(registrationId, intervalMs = 3000) {
          while (true) {
            const res = await fetch(
              `https://api.godaddy.com/v3/domains/registrations/${registrationId}`,
              { headers: { Authorization: `Bearer ${process.env.GODADDY_PAT}` } }
            );
            const { status, domain } = await res.json();
            if (status === "COMPLETED") return domain;
            if (status === "FAILED") throw new Error("Registration failed");
            await new Promise((r) => setTimeout(r, intervalMs));
          }
        }
        ```
      </Tab>

      <Tab value="Python">
        ```python
        import os, time, requests

        def poll_registration(registration_id, interval=3):
            while True:
                res = requests.get(
                    f"https://api.godaddy.com/v3/domains/registrations/{registration_id}",
                    headers={"Authorization": f"Bearer {os.environ['GODADDY_PAT']}"},
                )
                data = res.json()
                if data["status"] == "COMPLETED":
                    return data["domain"]
                if data["status"] == "FAILED":
                    raise RuntimeError("Registration failed")
                time.sleep(interval)
        ```
      </Tab>

      <Tab value="Go">
        ```go
        package main

        import (
            "encoding/json"
            "fmt"
            "net/http"
            "os"
            "time"
        )

        func pollRegistration(registrationID string) {
            for {
                req, _ := http.NewRequest("GET",
                    "https://api.godaddy.com/v3/domains/registrations/"+registrationID, nil)
                req.Header.Set("Authorization", "Bearer "+os.Getenv("GODADDY_PAT"))
                res, _ := http.DefaultClient.Do(req)

                var data struct {
                    Status string `json:"status"`
                }
                json.NewDecoder(res.Body).Decode(&data)
                res.Body.Close()

                switch data.Status {
                case "COMPLETED":
                    fmt.Println("Registration complete")
                    return
                case "FAILED":
                    panic("Registration failed")
                }
                time.Sleep(3 * time.Second)
            }
        }
        ```
      </Tab>
    </>
  </Tabs>

<Callout type="info" title="Operation statuses">
  | Status      | Meaning                                                       |
  | ----------- | ------------------------------------------------------------- |
  | `CONFIRMED` | Accepted, processing in progress. Keep polling.               |
  | `EXECUTING` | Actively executing. Keep polling.                             |
  | `COMPLETED` | Terminal — succeeded. `domain` and `expiresAt` are populated. |
  | `FAILED`    | Terminal — failed.                                            |

  You can also poll through `GET /v3/domains/operations/{operationId}`. Both endpoints resolve the same resource.
</Callout>

## Use the CLI

After you [set up the CLI](https://developer.godaddy.com/docs/api-users/cli-setup):

1. Check availability:

   ```bash
   gddy domain available example.com
   ```

2. Review the required agreements for the TLD:

   ```bash
   gddy domain agreements --tld com
   ```

3. Inspect TLD-specific schema requirements:

   ```bash
   gddy domain schema com
   ```

4. Register the domain:

   ```bash
   gddy domain purchase example.com --agree --confirm
   ```

   Registration is paid and not reversible. Run without `--agree` first to review the required agreements, and use `--dry-run` to preview before confirming.

   Use `gddy domain contacts init` to save default contacts for repeated purchases — the command writes a local `contacts.toml` template that the purchase command can use.

## Registration body fields

The following table lists the fields required for the registration body.

| Field                    | Required | Description                                                                                                                                             |
| ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `quoteToken`             | Yes      | Token from `POST /v3/domains/registration-quotes`.                                                                                                      |
| `domain`                 | Yes      | Fully-qualified domain name to register.                                                                                                                |
| `period`                 | Yes      | Registration period in years (must match the quoted period).                                                                                            |
| `consent`                | Yes      | ICANN consent object capturing the agreement type, consenting party, and timestamp listed in the [Consent object fields](#consent-object-fields) table. |
| `Idempotency-Key` header | Yes      | UUID generated per request.                                                                                                                             |

## Consent object fields

The following table lists the fields required for the consent object.

| Field            | Required | Description                                                                      | Note                                                                 |
| ---------------- | -------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| `agreementTypes` | Yes      | Agreement type keys from the TLD's required agreements (for example, `API_DPA`). | Get these from the `requiredAgreements` field in the quote response. |
| `agreedAt`       | Yes      | Actual ISO 8601 timestamp of when the user consented.                            |                                                                      |

## Common errors

The following table lists common errors and recommended actions. Go to [Errors](https://developer.godaddy.com/docs/api-users/errors) for the full error envelope.

| Status                       | Most likely cause                                                                                                      | Recommended action                                                     |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `400`                        | Malformed request body or missing required field.                                                                      | Inspect `fields[]` in the error response.                              |
| `403`                        | Credential lacks write access, no [payment method on file](https://developer.godaddy.com/docs/api-users/payment-profile), or account billing issue. | Check `code` to distinguish.                                           |
| `404`                        | TLD not supported.                                                                                                     |                                                                        |
| `409`                        | Domain is already registered (by anyone).                                                                              |                                                                        |
| `422 QUOTE_MISMATCH`         | The `period` or payment profile in the execute body doesn't match the quote.                                           | Re-use the same `period` from the quote, or fetch a new quote.         |
| `422 INVALID_AGREEMENT_KEYS` | Wrong `agreementTypes` value.                                                                                          | Use the keys returned in `requiredAgreements` from the quote response. |
| `422 MISSING_CONTACT`        | No registrant contact on the account profile.                                                                          | Add a registrant contact to the account profile.                       |
| `422 MISSING_BILLING_PHONE`  | Contact phone is missing or malformed.                                                                                 | Verify `phone.countryCode` and `phone.nationalNumber` are both set.    |
| `422`                        | Domain isn't available, missing or invalid consent, or other contact validation failure.                               | Inspect `fields[]` for specifics.                                      |

## Additional information

* [v3 Registrations — `POST /v3/domains/registrations`](https://developer.godaddy.com/docs/references/rest/domains/v3/registrations)
* [v3 Registration Quotes — `POST /v3/domains/registration-quotes`](https://developer.godaddy.com/docs/references/rest/domains/v3/registration-quotes)
* [v3 Operations — `GET /v3/domains/operations/{operationId}`](https://developer.godaddy.com/docs/references/rest/domains/v3/operations)
* [v1 registration and renewals — `POST /v1/domains/purchase`](https://developer.godaddy.com/docs/references/rest/domains/v1/register-and-renew-domains)
