godaddy-python-sdk
A small, typed Python client for the GoDaddy Domains and DNS API. It works with both sync and async code, retries when GoDaddy rate limits you, and returns plain dataclasses, not raw JSON.
from godaddy import GoDaddy
with GoDaddy("API_KEY", "API_SECRET") as gd:
gd.dns.point("example.com", "shop", "203.0.113.10")
That call makes shop.example.com resolve to 203.0.113.10.
Install
pip install godaddy-python-sdk
Requires Python 3.10 or newer. Dependencies: httpx, goodconf, pydantic and PyYAML.
Configuration
Settings are loaded with goodconf, so you can configure the client from code, from environment variables or from a config file. Values passed in code win over the environment, and the environment wins over the file.
Create a key pair at developer.godaddy.com/keys, then pick whichever option suits you:
gd = GoDaddy("API_KEY", "API_SECRET")
A personal access token (gd_pat_…) works too. It is sent as Authorization: Bearer …:
gd = GoDaddy(api_token="gd_pat_...") # or GoDaddy("gd_pat_..."), or GODADDY_API_TOKEN
export GODADDY_API_KEY=...
export GODADDY_API_SECRET=...
api_key = "..."
api_secret = "..."
environment = "production"
timeout = 15
With no arguments, the client looks for godaddy.toml, godaddy.yaml, godaddy.yml or godaddy.json in the working directory. To load a file from somewhere else, set GODADDY_CONFIG=/path/to/file, or pass config_file= to the client. YAML files need the yaml extra, and TOML files on Python 3.10 need the toml extra:
pip install "godaddy-python-sdk[yaml]"
| Setting | Env variable | Default |
|---|---|---|
api_key |
GODADDY_API_KEY |
|
api_secret |
GODADDY_API_SECRET |
|
api_token |
GODADDY_API_TOKEN |
|
environment |
GODADDY_ENVIRONMENT |
production |
base_url |
GODADDY_BASE_URL |
taken from environment |
production_url |
GODADDY_PRODUCTION_URL |
https://api.godaddy.com |
ote_url |
GODADDY_OTE_URL |
https://api.ote-godaddy.com |
api_version |
GODADDY_API_VERSION |
v1 |
endpoints.* |
GODADDY_ENDPOINTS__<NAME> |
see below |
shopper_id |
GODADDY_SHOPPER_ID |
|
timeout |
GODADDY_TIMEOUT |
30 |
max_retries |
GODADDY_MAX_RETRIES |
3 |
max_retry_wait |
GODADDY_MAX_RETRY_WAIT |
60 |
backoff_base |
GODADDY_BACKOFF_BASE |
0.5 |
backoff_cap |
GODADDY_BACKOFF_CAP |
8 |
page_size |
GODADDY_PAGE_SIZE |
100 |
user_agent |
GODADDY_USER_AGENT |
godaddy-python-sdk/{version} |
Any setting can also be passed straight to the client, for example GoDaddy(timeout=10, max_retries=5). If you build settings yourself, hand them over with settings=:
from godaddy import GoDaddy, load_settings
settings = load_settings("/etc/myapp/godaddy.toml", environment="ote")
gd = GoDaddy(settings=settings)
Endpoints
API paths are part of the settings too, so nothing is pinned in the code. Each one is a template relative to the base URL. {version} comes from api_version, and the other placeholders are filled in and URL encoded for each call.
| Name | Default |
|---|---|
domains |
/{version}/domains |
domain |
/{version}/domains/{domain} |
available |
/{version}/domains/available |
suggest |
/{version}/domains/suggest |
tlds |
/{version}/domains/tlds |
records |
/{version}/domains/{domain}/records |
records_by_type |
/{version}/domains/{domain}/records/{type} |
record |
/{version}/domains/{domain}/records/{type}/{name} |
You can override one of them in a config file:
[endpoints]
records = "/{version}/domains/{domain}/records"
or through the environment, with a double underscore between the parts:
export GODADDY_ENDPOINTS__TLDS="/{version}/domains/tlds"
GoDaddy gives you two environments. production is the default. ote is their test sandbox, and it needs its own keys.
Heads up: GoDaddy only opens the production Domains API to some account types. If every call comes back with PermissionDeniedError, check your account's API access first.
DNS
Every DNS method takes the domain first. Record names are relative to the domain, and the apex is @.
from godaddy import GoDaddy, DNSRecord
gd = GoDaddy()
gd.dns.list("example.com")
gd.dns.list("example.com", "A")
gd.dns.list("example.com", "A", "www")
gd.dns.get("example.com", "CNAME", "blog")
gd.dns.exists("example.com", "A", "shop")
Each of these returns a list of DNSRecord objects:
DNSRecord(type='A', name='www', data='203.0.113.10', ttl=600, ...)
Creating and changing records
gd.dns.add(
"example.com",
DNSRecord.a("api", "203.0.113.20"),
DNSRecord.txt("@", "google-site-verification=abc"),
DNSRecord.mx("@", "mail.example.com", priority=10),
)
gd.dns.replace("example.com", "A", "www", ["203.0.113.10", "203.0.113.11"], ttl=600)
gd.dns.delete("example.com", "A", "old")
gd.dns.delete("example.com", "A", "maybe-gone", missing_ok=True)
add appends records. replace swaps out every record with that type and name. Use replace_type to rewrite all records of one type, or replace_all to rewrite the whole zone. Be careful with replace_all: anything you leave out is removed.
Shortcuts
gd.dns.point("example.com", "shop", "203.0.113.10")
gd.dns.point("example.com", "shop", "2001:db8::10")
gd.dns.alias("example.com", "docs", "example.github.io")
gd.dns.set_txt("example.com", "_acme-challenge", "token-value", ttl=600)
gd.dns.unpoint("example.com", "shop")
point picks A or AAAA from the address you pass. unpoint deletes the record and does nothing if it is already gone.
Example: one subdomain per customer
from godaddy import GoDaddy, GoDaddyError
SERVER_IP = "203.0.113.10"
def create_customer_site(slug: str) -> str:
with GoDaddy() as gd:
if gd.dns.exists("example.com", "A", slug):
raise ValueError(f"{slug}.example.com is already taken")
gd.dns.point("example.com", slug, SERVER_IP)
return f"{slug}.example.com"
Domains
for domain in gd.domains.iter():
print(domain.domain, domain.expires, domain.renew_auto)
info = gd.domains.get("example.com")
info.name_servers
check = gd.domains.available("my-new-idea.com")
if check.available:
print(check.price, check.currency)
gd.domains.available_many(["one.com", "two.net"])
gd.domains.suggest("coffee shop baku", limit=10)
gd.domains.tlds()
gd.domains.update("example.com", renew_auto=True, locked=True)
available() reports prices in normal currency units as a Decimal. GoDaddy itself sends them in micro units.
Async
AsyncGoDaddy has the same methods. Await them.
import asyncio
from godaddy import AsyncGoDaddy
async def main():
async with AsyncGoDaddy() as gd:
records = await gd.dns.list("example.com", "A")
await gd.dns.point("example.com", "shop", "203.0.113.10")
async for domain in gd.domains.iter():
print(domain.domain)
asyncio.run(main())
Errors
All exceptions inherit from GoDaddyError.
| Exception | When |
|---|---|
ValidationError |
400 or 422, the request body or parameters were rejected |
AuthenticationError |
401, or no credentials were found |
PermissionDeniedError |
403 |
NotFoundError |
404 |
ConflictError |
409 |
RateLimitError |
429 that is still failing after retries. Has .retry_after |
ServerError |
5xx |
APIConnectionError / APITimeoutError |
the request never got an answer |
Status errors carry status_code, GoDaddy's code, a fields list that says which field failed and why, and the raw response.
from godaddy import ValidationError
try:
gd.dns.add("example.com", DNSRecord.a("bad name", "203.0.113.1"))
except ValidationError as exc:
for field in exc.fields:
print(field.path, field.message)
Retries and timeouts
GoDaddy allows about 60 requests per minute. When it answers 429, the client waits as long as GoDaddy asks and tries again. Server errors and dropped connections are retried with exponential backoff, but only for idempotent methods (GET, PUT, DELETE). A PATCH that adds records is never sent twice.
gd = GoDaddy(timeout=10, max_retries=5, max_retry_wait=30)
Set max_retries=0 to turn retries off. To use proxies or a custom transport, pass your own httpx.Client or httpx.AsyncClient as http_client=.
Reseller accounts can act for a customer by passing shopper_id=.
Messages
Every error and validation message the SDK produces is stored in godaddy/messages.yaml, so none of them are hardcoded. You can replace some or all of them, for example to translate them:
value:
empty_name: Record adı boş ola bilməz
error:
timeout: "Sorğu vaxtı bitdi: {error}"
from godaddy import messages
messages.use("messages.az.yaml")
messages.use({"value.empty_name": "Record adı boş ola bilməz"})
Unknown keys raise KeyError, which catches typos early. Call messages.reset() to go back to the defaults.
Development
python -m venv .venv
. .venv/bin/activate
pip install -e ".[dev]"
pytest
ruff check .
The tests use httpx.MockTransport, so they never touch the real API.
Links
- Source code: github.com/qiyascc/godaddy-python-sdk
- PyPI: pypi.org/project/godaddy-python-sdk
- Project page: theqiyas.cc/projects/godaddy-python-sdk
- Author: Qiyas Sadıqov, more projects at theqiyas.cc
License
MIT
Metadata
Release files for godaddy-python-sdk 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| godaddy_python_sdk-0.1.0.tar.gz | 20.7 kB | Without provenance | Details for godaddy_python_sdk-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| godaddy_python_sdk-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for godaddy_python_sdk-0.1.0-py3-none-any.whl |
Total release size: 40.5 kB
Release files / godaddy_python_sdk-0.1.0.tar.gz
| Download URL | godaddy_python_sdk-0.1.0.tar.gz |
|---|---|
| Size | 20.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
84d9c07d0152f19407ba0918ccab80f69fd81e3a6679653b05ad51c03d00222e
|
|
BLAKE2b-256 checksum How to use checksums |
8a08826ae1d8aaa74809dfd3ad7418a632985aa7de61f8e64c251f3b2528d4bf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / godaddy_python_sdk-0.1.0-py3-none-any.whl
| Download URL | godaddy_python_sdk-0.1.0-py3-none-any.whl |
|---|---|
| Size | 19.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8fb0cc1c751d1de8e7b5fdbc46ba3b740e0e5604f9a7fb15d993654c2de4d9e7
|
|
BLAKE2b-256 checksum How to use checksums |
dcde2a21e5b41e18eea4ad42cffd40f7c050d7600b8a08aa4bc33ba53cd75df5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Security for godaddy-python-sdk 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.