Domain management that doesn't stink! π½
A delightfully Pythonic library for managing domains at Porkbun. DNS records, DNSSEC, SSL certificates, URL forwarding, and more. Async-first with sync wrappers, type-safe, and thoroughly tested.
π Full Documentation | π½ Not affiliated with Porkbun
- π Async-first design - Built on httpx for modern async/await support
- π Type-safe records - Dataclasses with validation for all DNS record types
- π Sync wrappers - Use
Pigletwhen you don't need async - π» CLI included - Manage DNS from the command line
- π Auto-retry - Exponential backoff for transient failures
- π Python 3.13+ - Modern Python with full type annotations
pip install oinkerFor CLI support:
pip install "oinker[cli]"Set your Porkbun API credentials:
export PORKBUN_API_KEY="pk1_..."
export PORKBUN_SECRET_KEY="sk1_..."from oinker import AsyncPiglet, ARecord
async with AsyncPiglet() as piglet:
# Test connection
pong = await piglet.ping()
print(f"Your IP: {pong.your_ip}")
# List DNS records
records = await piglet.dns.list("example.com")
for record in records:
print(f"{record.record_type} {record.name} -> {record.content}")
# Create an A record
record_id = await piglet.dns.create(
"example.com",
ARecord(content="1.2.3.4", name="www")
)
# Delete by ID
await piglet.dns.delete("example.com", record_id=record_id)from oinker import Piglet, ARecord
with Piglet() as piglet:
pong = piglet.ping()
print(f"Your IP: {pong.your_ip}")
records = piglet.dns.list("example.com")# Test connection
$ oinker ping
π· Oink! Connected successfully.
Your IP: 203.0.113.42
# List DNS records
$ oinker dns list example.com
ββββββββββ¬ββββββββββββββββββ¬βββββββ¬ββββββββββββ¬ββββββ
β ID β Name β Type β Content β TTL β
ββββββββββΌββββββββββββββββββΌβββββββΌββββββββββββΌββββββ€
β 123456 β example.com β A β 1.2.3.4 β 600 β
β 123457 β www.example.com β A β 1.2.3.4 β 600 β
ββββββββββ΄ββββββββββββββββββ΄βββββββ΄ββββββββββββ΄ββββββ
# Create an A record
$ oinker dns create example.com A www 1.2.3.4
π· Squeee! Created record 123458
# Delete a record
$ oinker dns delete example.com --id 123458
π· Gobbled up record 123458Oinker provides type-safe dataclasses for all Porkbun-supported record types:
from oinker import (
ARecord, # IPv4 address
AAAARecord, # IPv6 address
MXRecord, # Mail server
TXTRecord, # Text record
CNAMERecord, # Canonical name
ALIASRecord, # ALIAS/ANAME
NSRecord, # Name server
SRVRecord, # Service record
TLSARecord, # DANE/TLSA
CAARecord, # CA Authorization
HTTPSRecord, # HTTPS binding
SVCBRecord, # Service binding
SSHFPRecord, # SSH fingerprint
)Records validate their content on construction:
from oinker import ARecord, ValidationError
try:
ARecord(content="not-an-ip")
except ValidationError as e:
print(e) # Invalid IPv4 address: not-an-ipasync with AsyncPiglet() as piglet:
# List all domains
domains = await piglet.domains.list()
# Get/update nameservers
ns = await piglet.domains.get_nameservers("example.com")
await piglet.domains.update_nameservers("example.com", [
"ns1.example.com",
"ns2.example.com",
])
# URL forwarding
forwards = await piglet.domains.get_url_forwards("example.com")
# Check domain availability
availability = await piglet.domains.check("example.com")from oinker import DNSSECRecordCreate
async with AsyncPiglet() as piglet:
# List DNSSEC records
records = await piglet.dnssec.list("example.com")
# Create DNSSEC record
await piglet.dnssec.create("example.com", DNSSECRecordCreate(
key_tag="64087",
algorithm="13",
digest_type="2",
digest="15E445BD...",
))async with AsyncPiglet() as piglet:
bundle = await piglet.ssl.retrieve("example.com")
print(bundle.certificate_chain)
print(bundle.private_key)from oinker import (
AsyncPiglet,
OinkerError,
AuthenticationError,
RateLimitError,
NotFoundError,
)
async with AsyncPiglet() as piglet:
try:
await piglet.dns.list("example.com")
except AuthenticationError:
print("Check your API credentials")
except NotFoundError:
print("Domain not found or API access not enabled")
except RateLimitError as e:
print(f"Slow down! Retry after {e.retry_after}s")
except OinkerError as e:
print(f"API error: {e}")For more examples and detailed API reference, check out the full documentation.
This project is not affiliated with Porkbun in any way. It's just a passion project by someone who really, really loves Porkbun. They're amazing! π½
MIT