| name | godaddy-api |
| description | Manage GoDaddy domains and DNS records via the official GoDaddy Developer Portal REST API (developer.godaddy.com). Use when user needs to list domains, update DNS records, check domain availability, or automate domain management tasks. |
| source | https://developer.godaddy.com/doc/endpoint/domains (Swagger spec) |
GoDaddy API — Agent Skill
Manage domains and DNS records through the official GoDaddy Developer Portal REST API. Documentation source: developer.godaddy.com.
Prerequisites
- GoDaddy account with at least 1 domain (Management/DNS API access requires an active domain)
- API Key + Secret — Generate at https://developer.godaddy.com/keys
- First key created is for OTE (sandbox) testing against
api.ote-godaddy.com
- Create a second key for production against
api.godaddy.com
- Domain availability API requires account with 50+ domains
- Python 3.9+ with
requests and python-dotenv, or Node.js 18+ (native fetch)
Authentication
GoDaddy uses SSO key-based auth. Every request includes this header:
Authorization: sso-key {API_KEY}:{API_SECRET}
For resellers operating on behalf of customers, add X-Shopper-Id header with the customer's Shopper ID.
Environment Variables
export GODADDY_API_KEY="your_key"
export GODADDY_API_SECRET="your_secret"
export GODADDY_API_ENV="production"
Base URLs
| Environment | Base URL | Key Type |
|---|
| Production | https://api.godaddy.com | Production key |
| OTE Sandbox | https://api.ote-godaddy.com | OTE/test key |
Note: The /v1 prefix is part of each endpoint path, not the base URL.
API Endpoints Reference (from official Swagger spec)
Domain Operations
| Method | Path | Operation ID | Purpose |
|---|
| GET | /v1/domains | list | List domains for the shopper (max 1000 per page, use marker for pagination) |
| GET | /v1/domains/{domain} | get | Get full domain details (status, nameservers, contacts, expiration) |
| PATCH | /v1/domains/{domain} | update | Update domain settings (e.g., nameservers, auto-renew) |
| DELETE | /v1/domains/{domain} | cancel | Cancel a purchased domain |
| GET | /v1/domains/available | available | Check domain availability (requires 50+ domains on account) |
| GET | /v1/domains/suggest | suggest | Suggest alternate domain names based on seed/keywords |
| GET | /v1/domains/tlds | tlds | List TLDs supported for sale |
| GET | /v1/domains/agreements | getAgreement | Retrieve legal agreements for TLDs |
| POST | /v1/domains/purchase | purchase | Purchase a domain |
| POST | /v1/domains/{domain}/renew | renew | Renew a domain |
| POST | /v1/domains/{domain}/transfer | transferIn | Transfer a domain in |
DNS Record Operations (primary use case)
| Method | Path | Operation ID | Purpose |
|---|
| PATCH | /v1/domains/{domain}/records | recordAdd | Add records — appends to existing zone |
| PUT | /v1/domains/{domain}/records | recordReplace | Replace ALL records in the entire zone |
| GET | /v1/domains/{domain}/records/{type}/{name} | recordGet | Retrieve records by type + name (supports offset & limit query params) |
| PUT | /v1/domains/{domain}/records/{type}/{name} | recordReplaceTypeName | Replace all records matching type + name |
| PUT | /v1/domains/{domain}/records/{type} | recordReplaceType | Replace all records of a given type |
| DELETE | /v1/domains/{domain}/records/{type}/{name} | recordDeleteTypeName | Delete all records matching type + name |
DNS Record Types
- GET/PUT accept:
A, AAAA, CNAME, MX, NS, SOA, SRV, TXT
- DELETE accepts:
A, AAAA, CNAME, MX, SRV, TXT (cannot delete NS or SOA)
Other Domain Operations
| Method | Path | Purpose |
|---|
| PATCH | /v1/domains/{domain}/contacts | Update domain contacts |
| DELETE | /v1/domains/{domain}/privacy | Cancel privacy protection |
| POST | /v1/domains/{domain}/privacy/purchase | Purchase privacy protection |
| POST | /v1/domains/{domain}/verifyRegistrantEmail | Re-send registrant verification email |
DNS Record Schema (from Swagger #/definitions/DNSRecord)
{
"type": "A",
"name": "api",
"data": "1.2.3.4",
"ttl": 600,
"priority": 10,
"port": 443,
"protocol": "_tcp",
"service": "_https",
"weight": 100
}
When using PUT /records/{type}/{name}, the body uses DNSRecordCreateTypeName which omits type and name (they're in the URL):
[{ "data": "1.2.3.4", "ttl": 600 }]
When using PUT /records/{type}, the body uses DNSRecordCreateType which omits type (it's in the URL):
[{ "name": "api", "data": "1.2.3.4", "ttl": 600 }]
Python Client
pip install requests python-dotenv
"""GoDaddy Developer Portal API client — sourced from developer.godaddy.com Swagger spec."""
import os, time
from typing import Any, Dict, List, Optional
import requests
from dotenv import load_dotenv
load_dotenv()
class GoDaddyClient:
"""Wrapper around the official GoDaddy Domains REST API."""
BASE_URLS = {
"production": "https://api.godaddy.com",
"ote": "https://api.ote-godaddy.com",
}
def __init__(
self,
api_key: Optional[str] = None,
api_secret: Optional[str] = None,
environment: str = "production",
timeout: int = 30,
shopper_id: Optional[str] = None,
) -> None:
self.api_key = api_key or os.getenv("GODADDY_API_KEY", "")
self.api_secret = api_secret or os.getenv("GODADDY_API_SECRET", "")
env = (environment or os.getenv("GODADDY_API_ENV", "production")).lower()
self.timeout = timeout
.base_url = .BASE_URLS[env]
.shopper_id = shopper_id
.session = requests.Session()
.api_key .api_secret:
ValueError()
() -> [, ]:
h = {
: ,
: ,
: ,
}
.shopper_id:
h[] = .shopper_id
h
() -> :
url =
resp = .session.request(
method, url, headers=._headers, timeout=.timeout, **kwargs
)
resp.status_code == :
retry = (resp.headers.get(, ))
RuntimeError()
resp.ok:
RuntimeError()
resp.status_code == resp.content:
resp.json()
() -> []:
params = {: (limit, )}
marker:
params[] = marker
._request(, , params=params)
() -> :
._request(, )
() -> :
._request(, , json=updates)
() -> :
._request(, , params={: domain})
() -> []:
path =
record_type:
path +=
name:
path +=
params = {}
offset :
params[] = offset
limit :
params[] = limit
._request(, path, params=params )
() -> :
._request(, , json=records)
() -> :
._request(, , json=records)
() -> :
._request(
, , json=records
)
() -> :
._request(
, , json=records
)
() -> :
._request(
,
)
Usage Examples
client = GoDaddyClient()
for d in client.list_domains():
print(f"{d['domain']} — status: {d['status']}, expires: {d.get('expires', 'N/A')}")
records = client.list_records("example.com")
for r in records:
print(f"{r['type']:6} {r['name']:20} → {r['data']} (TTL: {r.get('ttl', 'default')})")
api_records = client.list_records("example.com", record_type="A", name="api")
client.add_records("example.com", [
{"type": "A", "name": "api", "data": "134.209.221.255", "ttl": 600}
])
client.replace_records_by_type_name("example.com", "CNAME", "app", [
{"data": "my-app.netlify.app", "ttl": 3600}
])
client.delete_records(, , )
result = client.check_availability()
()
client.update_domain(, {
: [, ]
})
Node.js / TypeScript Client
No dependencies — uses native fetch (Node 18+):
class GoDaddyClient {
private baseUrl: string;
private headers: Record<string, string>;
constructor(
apiKey = process.env.GODADDY_API_KEY!,
apiSecret = process.env.GODADDY_API_SECRET!,
env = process.env.GODADDY_API_ENV || "production",
shopperId?: string,
) {
this.baseUrl =
env === "ote"
? "https://api.ote-godaddy.com"
: "https://api.godaddy.com";
this.headers = {
Authorization: `sso-key ${apiKey}:${apiSecret}`,
"Content-Type": "application/json",
Accept: "application/json",
};
if (shopperId) this.headers["X-Shopper-Id"] = shopperId;
}
private async request<T = any>(
method: string,
path: string,
body?: unknown,
): Promise<T | null> {
const resp = await fetch(``, {
method,
: .,
: body ? .(body) : ,
});
(resp. === ) {
retry = resp..() || ;
();
}
(!resp.)
();
(resp. === ) ;
text = resp.();
text ? .(text) : ;
}
listDomains =
.(, );
getDomain =
.(, );
updateDomain =
.(, , updates);
checkAvailability =
.(, );
listRecords = {
path = ;
() path += ;
( && name) path += ;
.(, path);
};
addRecords =
.(, , records);
replaceAllRecords =
.(, , records);
replaceRecordsByTypeName =
.(, , records);
replaceRecordsByType =
.(, , records);
deleteRecords =
.(, );
}
curl Examples
AUTH="Authorization: sso-key $GODADDY_API_KEY:$GODADDY_API_SECRET"
BASE="https://api.godaddy.com"
curl -s -H "$AUTH" "$BASE/v1/domains?limit=100" | jq '.[].domain'
curl -s -H "$AUTH" "$BASE/v1/domains/example.com" | jq
curl -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
"$BASE/v1/domains/example.com" \
-d '{"nameServers":["ns1.cloudflare.com","ns2.cloudflare.com"]}'
curl -s -H "$AUTH" "$BASE/v1/domains/available?domain=coolstartup.io" | jq
curl -s -H "$AUTH" "$BASE/v1/domains/example.com/records" | jq
curl -s -H "$AUTH" "$BASE/v1/domains/example.com/records/A" | jq
curl -s -H "$AUTH" "$BASE/v1/domains/example.com/records/A/api" | jq
curl -X PATCH -H -H \
\
-d
curl -X PUT -H -H \
\
-d
curl -X PUT -H -H \
\
-d
curl -X DELETE -H
curl -X PATCH -H -H \
\
-d
Common Patterns
Point subdomain to a new server IP
client.replace_records_by_type_name("mysite.com", "A", "api", [
{"data": "NEW_SERVER_IP", "ttl": 600}
])
Add a Netlify CNAME for frontend
client.add_records("mysite.com", [
{"type": "CNAME", "name": "app", "data": "my-app.netlify.app", "ttl": 3600}
])
Add email DNS (MX + SPF + DKIM)
client.replace_records_by_type_name("mysite.com", "MX", "@", [
{"data": "inbound-smtp.us-east-2.amazonaws.com", "priority": 10, "ttl": 3600}
])
client.add_records("mysite.com", [
{"type": "TXT", "name": "@", "data": "v=spf1 include:amazonses.com ~all", "ttl": 3600}
])
Switch nameservers to Cloudflare
client.update_domain("mysite.com", {
"nameServers": ["ns1.cloudflare.com", "ns2.cloudflare.com"]
})
Batch add multiple records safely
import time
records_to_add = [
{"type": "A", "name": "api", "data": "1.2.3.4", "ttl": 600},
{"type": "CNAME", "name": "app", "data": "my-app.netlify.app", "ttl": 3600},
{"type": "TXT", "name": "@", "data": "v=spf1 include:amazonses.com ~all", "ttl": 3600},
{"type": "MX", "name": "@", "data": "mail.example.com", "priority": 10, "ttl": 3600},
]
client.add_records("mysite.com", records_to_add)
Rate Limits & Access Restrictions
| Restriction | Limit |
|---|
| Requests per minute | 60 per endpoint |
| Zone record limit (standard DNS) | 500 records per zone |
| Zone record limit (premium DNS) | 1,500 records per zone |
| GET /records limit param | Max 500 records per response |
| GET /domains limit param | Max 1,000 domains per response |
| Availability API access | Requires 50+ domains on account |
| DNS/Management API access | Requires 1+ domain on account |
When rate limited, the API returns 429 Too Many Requests with a Retry-After header.
Pitfalls & Critical Warnings
-
PATCH adds, PUT replaces — PATCH /records safely appends. PUT /records replaces the ENTIRE zone (will delete any records not in the request body). PUT /records/{type}/{name} replaces only the matching records. Always prefer PATCH to add and PUT /records/{type}/{name} to update specific records.
-
TTL minimum is 600 — API returns 422 if you set a lower TTL.
-
Cannot delete NS or SOA — The DELETE endpoint only accepts A, AAAA, CNAME, MX, SRV, TXT.
-
OTE keys ≠ Production keys — Test keys only work against api.ote-godaddy.com. Production keys only against api.godaddy.com. They are not interchangeable.
-
Root domain = @ — Use @ as the name field for the zone apex (e.g., example.com itself).
-
No CNAME at root — GoDaddy does not support CNAME at @. Use an A record instead, or delegate nameservers to a provider that supports CNAME flattening (e.g., Cloudflare).
-
API access restricted in 2024 — GoDaddy silently restricted DNS API access. You must have at least one domain on the account. The Availability API requires 50+ domains.
-
PUT /records/{type}/{name} body schema — The body does NOT include type or name fields (they're in the URL path). Only send data, ttl, etc.
-
Rate limiting in batch operations — If you need to make many calls, add a 1-second delay between requests. A single PATCH call can add multiple records at once (preferred over multiple calls).
-
2FA blocks some operations — Updating nameservers on "protected" or "high-value" domains may require 2FA, which is not supported via the API. You must do this in the GoDaddy web UI.
HTTP Response Codes
| Code | Meaning |
|---|
| 200 | Success (with response body) |
| 204 | Success (no content — DELETE, some PUTs) |
| 400 | Malformed request |
| 401 | Invalid or missing authentication |
| 403 | Authenticated but not authorized |
| 404 | Domain or resource not found |
| 409 | Domain not eligible for this operation |
| 422 | Validation error (invalid domain, bad schema, TTL too low) |
| 429 | Rate limited — check Retry-After header |
| 500 | GoDaddy internal server error |
| 504 | Gateway timeout |