Skip to main content

A delightfully Pythonic library for managing DNS at Porkbun

Project description

๐Ÿท Oinker

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

CI codecov PyPI version Python 3.13+ License: MIT

Features

  • ๐Ÿš€ 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 Piglet when 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

๐Ÿ“ฆ Installation

pip install oinker

For CLI support:

pip install "oinker[cli]"

๐Ÿš€ Quick Start

Set your Porkbun API credentials:

export PORKBUN_API_KEY="pk1_..."
export PORKBUN_SECRET_KEY="sk1_..."

Async (Recommended)

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)

Sync

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")

CLI

# 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 123458

๐Ÿ“ DNS Record Types

Oinker 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-ip

๐ŸŒ Domain Operations

async 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")

๐Ÿ” DNSSEC

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...",
    ))

๐Ÿ”’ SSL Certificates

async with AsyncPiglet() as piglet:
    bundle = await piglet.ssl.retrieve("example.com")
    print(bundle.certificate_chain)
    print(bundle.private_key)

โš ๏ธ Error Handling

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}")

๐Ÿ“š Documentation

For more examples and detailed API reference, check out the full documentation.

๐Ÿท Disclaimer

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! ๐Ÿฝ

๐Ÿ“„ License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

oinker-0.1.1.tar.gz (28.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

oinker-0.1.1-py3-none-any.whl (44.2 kB view details)

Uploaded Python 3

File details

Details for the file oinker-0.1.1.tar.gz.

File metadata

  • Download URL: oinker-0.1.1.tar.gz
  • Upload date:
  • Size: 28.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for oinker-0.1.1.tar.gz
Algorithm Hash digest
SHA256 c6a87ee4727d7d097b3c48dfe01fb5f99588504a11c04c0367c08d7894bb6788
MD5 88bec4cdd2b47020b4517f45222b3412
BLAKE2b-256 9b1b3da3484b657c9f4855190897a2993be0840e96e0e7c20d08c0443de016ca

See more details on using hashes here.

Provenance

The following attestation bundles were made for oinker-0.1.1.tar.gz:

Publisher: publish.yml on major/oinker

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file oinker-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: oinker-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 44.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for oinker-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d81ceb8e60c8c50270e3a985538a9cb139e19a4172d53149bf83979bb8666b24
MD5 9d8ebf4f2bf0f4ec09e41753195f087e
BLAKE2b-256 d0e5f93ba3173ca297900c3be7f1cf9011ebe21bb1003c494292ddfcee008e2a

See more details on using hashes here.

Provenance

The following attestation bundles were made for oinker-0.1.1-py3-none-any.whl:

Publisher: publish.yml on major/oinker

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page