Skip to main content

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.

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)

Source distribution for godaddy-python-sdk 0.1.0
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)

Table of built distributions (wheels) for godaddy-python-sdk 0.1.0
File Interpreter ABI Platform Actions
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.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page