# Porkbun API v3 — v3.3

> Full Markdown reference for LLMs and agents. Machine-readable OpenAPI spec: https://porkbun.com/api/json/v3/spec
> Prefer a single topic? Per-area pages are indexed at https://porkbun.com/llms

The Porkbun API enables programmatic domain registration, DNS management, SSL certificate retrieval, and related operations. It is fully usable by AI agents, automation scripts, and developer tools.
## Quickstart

**1. Get your API keys** — visit https://porkbun.com/account/api

**2. Test connectivity**
```bash
curl https://api.porkbun.com/api/json/v3/ip
```
Returns your public IP. No credentials required.

**3. Check domain availability** (also verifies your credentials)
```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/checkDomain/example.com \
  -H "Content-Type: application/json" \
  -d '{"apikey":"your_api_key","secretapikey":"your_secret_key"}'
```
Returns `avail: "yes"/"no"` and `price` in USD. Returns an error if credentials are invalid.

**4. Register the domain** — convert `price` to pennies (e.g. $9.73 → 973) for `cost`
```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/create/example.com \
  -H "Content-Type: application/json" \
  -d '{"apikey":"your_api_key","secretapikey":"your_secret_key","cost":973,"agreeToTerms":"yes"}'
```

**5. Add a DNS record**
```bash
curl -X POST https://api.porkbun.com/api/json/v3/dns/create/example.com \
  -H "Content-Type: application/json" \
  -d '{"apikey":"your_api_key","secretapikey":"your_secret_key","type":"A","content":"1.2.3.4","ttl":"600"}'
```

## AI agents (MCP)

If you're building with Claude Desktop, Cursor, Cline, or another [Model Context Protocol](https://modelcontextprotocol.io) client, install our official Porkbun MCP server — no integration code required, your AI client gets domain registration, DNS, SSL, marketplace, and account-management tools natively.

```bash
npx -y @porkbunllc/mcp-server
```

Repo and Claude Desktop config: [github.com/oborseth/Porkbun-MCP](https://github.com/oborseth/Porkbun-MCP). 30 tools covering everything documented below. All write operations automatically attach an `Idempotency-Key` so agent retries don't double-charge.

## What you can build

- **Agentic domain registration** — Search availability and pricing across hundreds of TLDs, then register domains on behalf of users with a single API call
- **Automated DNS management** — Provision, update, and tear down DNS records as part of infrastructure automation or app deployment pipelines
- **Dynamic DNS clients** — Use `/ping` or `/ip` to detect IP address changes, then update A/AAAA records automatically
- **Domain portfolio tools** — List, monitor expiry, and configure auto-renewal settings across all domains in an account
- **SSL automation** — Retrieve free SSL certificate bundles for domains registered at Porkbun
- **Domain availability search** — Check availability and real-time pricing across all supported TLDs

## Agent-friendly design

- **Machine-readable error codes** — Every error response includes a `code` field (e.g. `INVALID_DOMAIN`, `INSUFFICIENT_FUNDS`) for programmatic branching
- **Header authentication** — Pass `X-API-Key` / `X-Secret-API-Key` as request headers; no JSON body required for read operations
- **GET support on read endpoints** — All read-only endpoints accept `GET` requests, making safe/idempotent operations distinguishable by HTTP method
- **Rate limit headers** — `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` on rate-limited endpoints enable intelligent backoff
- **Spec discovery** — Every API response includes `Link: <https://porkbun.com/api/json/v3/spec>; rel="describedby"` so clients can self-discover this spec
- **Idempotency keys** — Send `Idempotency-Key: <unique-string>` on POST endpoints; retries within 24h return the cached response so a network blip can't double-charge
- **Official MCP server** — `npx -y @porkbunllc/mcp-server` exposes this entire API as native tools for Claude Desktop, Cursor, and other Model Context Protocol clients
- **Request IDs** — every response carries an `X-Request-Id` header and a `requestId` field in the JSON body; reference these in support tickets, retry-deduplication logic, or log correlation
- **Version signalling** — every response carries an `X-API-Version` header (e.g. `3.2`). The URL path stays at `/api/json/v3/` across all minor versions; minor bumps are always backward-compatible, so you can pin to `v3` and watch the header (or the Changelog below) to know what's available
- **Per-key scoping** — restrict each API key to specific source IPs (with CIDR support) and/or specific target domains. Lets you hand an agent a key that can only operate on the domains you intend, from the network you expect
- **Plain-text docs for LLMs** — a short overview at [/llms.txt](https://porkbun.com/llms.txt) and the full reference as flat Markdown at [/llms-full.txt](https://porkbun.com/llms-full.txt), and per-topic pages at [/llms](https://porkbun.com/llms) (no JavaScript required, generated from this spec)
- **Dry run** — pass `dryRun: true` to `/domain/create`, `/domain/renew`, or `/domain/transfer` to run every pre-flight check and get back the would-be cost, balance, and `wouldSucceed` WITHOUT charging or creating anything. Rehearse a billable operation before committing

## Intended use

The Porkbun API is not a reseller service as defined under ICANN’s Registrar Accreditation Agreement (RAA). All domain registrations are processed directly by Porkbun as the registrar of record. The API is intended for managing domains within your own account or on behalf of clients, and does not establish a reseller relationship.

## Authentication

**1. JSON body (primary)** — Include `apikey` and `secretapikey` in the JSON request body. This is the standard method.

**2. Request headers** — Pass `X-API-Key: <apikey>` and `X-Secret-API-Key: <secretapikey>` as headers instead of in the body. Header auth takes effect only when no body credentials are present.

## HTTP status codes

- **400** — Request error (see `code` and `message` in response body)
- **403** — Additional authentication required (e.g. two-factor code)
- **429** — Rate limit exceeded (see `X-RateLimit-Reset` header for retry time)

## Error codes

Every error response includes a `code` string field alongside `status: "ERROR"` and `message`. Use `code` for programmatic error handling; use `message` for display to users.

**Authentication and protocol**

| Code | Meaning |
|------|---------|
| `INVALID_PROTOCOL` | Request was not made over HTTPS |
| `METHOD_NOT_ALLOWED` | HTTP method not allowed for this endpoint |
| `INVALID_OR_EMPTY_JSON` | Request body is missing or not valid JSON |
| `API_KEY_REQUIRED` | No API key or token was provided |
| `INVALID_API_KEYS_001` | API key and secret combination is invalid |
| `INVALID_TOKEN` | Bearer token is invalid or expired |
| `INVALID_USER` | Account associated with the API key was not found or is not active |
| `IP_NOT_ALLOWED` | This API key has an IP allowlist configured and the request source IP is not in it. HTTP 403. |
| `DOMAIN_NOT_ALLOWED` | This API key has a domain allowlist configured and the target domain is not in it. HTTP 403. |

**Rate limiting**

| Code | Meaning |
|------|---------|
| `RATE_LIMIT_EXCEEDED` | Request rate limit reached; see `ttlRemaining` field and `X-RateLimit-Reset` header for reset time |

**Domain operations**

| Code | Meaning |
|------|---------|
| `INVALID_DOMAIN` | Domain parameter is invalid or not in your account |
| `DOMAIN_NOT_AVAILABLE` | Domain is not available for registration |
| `INSUFFICIENT_FUNDS` | Account credit is insufficient to complete the purchase |

**DNS operations**

| Code | Meaning |
|------|---------|
| `INVALID_TYPE` | DNS record type is not supported |
| `INVALID_RECORD_ID` | DNS record ID was not found or is not owned by your account |

Additional endpoint-specific codes may be returned; always check `message` for details.

## API key scoping (IP &amp; domain restrictions)

Each API key can optionally be restricted to specific source IPs and/or specific target domains. Both restrictions are configured per key at [porkbun.com/account/api](https://porkbun.com/account/api) (click the gear icon next to any key). Empty/unset = no restriction.

**Source IP allowlist.** When set, requests from any other IP fail immediately with HTTP 403 `IP_NOT_ALLOWED`, before any other endpoint logic runs. Supports IPv4 and IPv6, both bare addresses and CIDR ranges. One entry per line in the UI. Examples:

```
203.0.113.10
198.51.100.0/24
2001:db8::/32
```

**Target domain allowlist.** When set, any operation against a domain not in the list fails with HTTP 403 `DOMAIN_NOT_ALLOWED`. Exact match only — `example.com` does not implicitly include `foo.example.com`, because each registered domain is independent. (Subdomains inside DNS records are scoped under the parent domain, which is what gets checked.) Example:

```
example.com
myothersite.io
```

**Recommended pattern for AI agents.** Hand the agent its own dedicated API key, restricted to the domains it actually needs to manage and (if you know the agent's egress IP) restricted to that IP. The blast radius of an accidentally-leaked key drops to `operations on these domains from this IP` instead of `anything on the account`.

## Idempotency

All v3 POST endpoints (excluding partner-only routes) accept an optional `Idempotency-Key` request header. When present, the API stores the response for 24 hours and replays it for any retry of the same request — so an agent that retries after a network blip will not double-charge or double-register.

**Key format:** any non-empty string up to 255 characters. UUIDs work well; agents can also use their own internal request IDs.

**Behavior on retry:**

- **Same key, same request body** within 24h → returns the original response with header `Idempotent-Replayed: true`
- **Same key, different request body** → returns 409 with `code: IDEMPOTENCY_KEY_MISMATCH` (catches accidental key reuse)
- **Same key, original request still in flight** → returns 409 with `code: IDEMPOTENCY_KEY_IN_USE` (retry shortly)
- **No header sent** → behavior is unchanged from before; no caching happens

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/create/example.com \
  -H "Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "X-API-Key: pk1_..." -H "X-Secret-API-Key: sk1_..." \
  -d '{"cost":973,"agreeToTerms":"yes"}'
```

## Rate limiting

Some endpoints are rate limited. When exceeded, the API returns HTTP 429 with a `RateLimitExceeded` response body. Fixed-limit endpoints (`/apikey/request`, `/apikey/retrieve`) also return `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers on every call.

## Webhooks

Subscribe HTTPS endpoints to account events and Porkbun will `POST` a signed JSON payload to them as those events happen — no polling required. Manage endpoints with the **Webhooks** endpoints in this reference, the MCP tools (`create_webhook`, `list_webhooks`, …), or the web UI at `porkbun.com/account/api`.

**Event types:** `domain.registered`, `domain.renewed`, `domain.transfer.completed`, `domain.expiring` (fires at 60/30/5 days before expiry), `dns.record.created`, `dns.record.updated`, `dns.record.deleted`. Subscribe to specific types, to a prefix wildcard like `dns.*`, or to `*` for everything (recommended — you'll receive new event types automatically). Call `GET /webhook/eventTypes` for the live catalog.

**Payload envelope** — every delivery has the same outer shape:

```json
{
  "event": "domain.registered",
  "id": "018f9c2a-7b3e-7c41-9b8a-2f1e6d4c5a90",
  "createdAt": "2026-06-17T18:30:00Z",
  "data": { "domain": "example.com", "tld": "com", "expireDate": "2027-06-17 18:30:00" }
}
```

`id` is a UUIDv7 (time-ordered) and is also sent as the `X-Porkbun-Webhook-Id` header; use it to dedupe, since an endpoint may occasionally receive the same event more than once. The `data` object is event-specific.

**Delivery headers:**

- `X-Porkbun-Event` — the event type (e.g. `domain.renewed`).
- `X-Porkbun-Webhook-Id` — the event UUID (matches `id` in the body).
- `X-Porkbun-Webhook-Timestamp` — Unix seconds when the request was signed.
- `X-Porkbun-Signature` — `sha256=` + the signature (see below).

**Verify the signature.** The signature is `HMAC-SHA256(secret, "{timestamp}.{rawBody}")` where `secret` is the endpoint's signing secret, `{timestamp}` is the `X-Porkbun-Webhook-Timestamp` header value, and `{rawBody}` is the exact bytes of the request body (verify before parsing). Compare using a constant-time equality check, and reject timestamps that are too old (e.g. >5 minutes) to blunt replay attacks.

```php
$timestamp = $_SERVER['HTTP_X_PORKBUN_WEBHOOK_TIMESTAMP'];
$signature = $_SERVER['HTTP_X_PORKBUN_SIGNATURE']; // "sha256=..."
$body      = file_get_contents('php://input');
$expected  = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $endpointSecret);
if (!hash_equals($expected, $signature) || abs(time() - (int)$timestamp) > 300) {
    http_response_code(400); exit;
}
// signature OK — now json_decode($body) and process
```

```javascript
import crypto from 'node:crypto';
const ts  = req.header('X-Porkbun-Webhook-Timestamp');
const sig = req.header('X-Porkbun-Signature');
const expected = 'sha256=' + crypto.createHmac('sha256', endpointSecret)
  .update(`${ts}.${rawBody}`).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))
  && Math.abs(Date.now()/1000 - Number(ts)) < 300;
```

**Delivery log & manual resend.** Every attempt is recorded — list recent deliveries with `GET /webhook/deliveries` (filter by `endpointId` or `status`, newest first) and fetch one with its full payload via `GET /webhook/delivery/{id}`. History is retained ~30 days. To replay a delivery (e.g. after fixing a bug on your side), call `POST /webhook/resend` with the delivery `id`; it re-queues a fresh attempt reusing the **original event id**, so a consumer that dedupes on `X-Porkbun-Webhook-Id` treats it as the same event. The target endpoint must still exist and be ACTIVE.

**Responding & retries.** Return any `2xx` status to acknowledge. Non-2xx responses, timeouts, or connection errors are retried with exponential backoff (~1m, 5m, 30m, 2h, 6h) up to 6 attempts. An endpoint that racks up 20 consecutive failures is automatically disabled and the account owner emailed; re-enable it (which resets the failure counter) via `POST /webhook/update` with `status: "ACTIVE"`. Use `POST /webhook/test` to send a `webhook.test` event and confirm your verifier works end-to-end.

## Changelog

The API URL path stays at `/api/json/v3/`. The version below (`major.minor`, also on the `X-API-Version` response header) tracks backward-compatible additions — new endpoints, new optional fields, new error codes, relaxed limits. Existing integrations are never broken by a minor bump; only a breaking change would introduce a new major version (and a new URL path).

### v3.3

- **Outbound webhooks** — register HTTPS endpoints that Porkbun POSTs signed JSON to when lifecycle events occur (`domain.registered`, `domain.renewed`, `domain.transfer.completed`, `domain.expiring`, `dns.record.created|updated|deleted`). Manage them under the **Webhooks** endpoints below or at `porkbun.com/account/api`. Each delivery is signed with HMAC-SHA256 — see the **Webhooks** section above.

### v3.2

- **Dry run** — `dryRun: true` on register/renew/transfer previews availability, cost, balance, and `wouldSucceed` without charging or creating anything.
- **LLM-readable docs** — full API reference as flat Markdown at `/llms-full.txt`, plus per-topic pages indexed at `/llms`, generated from this spec; refreshed `/llms.txt` index; non-JavaScript fallback on the docs page so agents can read it.

### v3.1

- **Idempotency** — `Idempotency-Key` request header on POST endpoints; retries within 24h replay the original response instead of re-charging.
- **Request IDs** — `X-Request-Id` response header and `requestId` body field on every response.
- **Version signalling** — `X-API-Version` response header.
- **Domain transfers** — `transfer`, `getTransfer`, and `listTransfers` endpoints for inbound transfers via API.
- **Single-domain lookup** — `GET /domain/get/{domain}` plus new filters on `listAll` (`domain`, `nameContains`, `tlds`, `expiringWithinDays`, `autoRenew`, `apiAccess`, `sortName`, `sortDirection`).
- **Marketplace filtering** — server-side `query`, `tlds`, `sldLengthMin`/`Max`, and sort parameters on `marketplace/getAll`.
- **Account endpoints** — `GET /account/balance` and `GET /account/apiSettings`.
- **Spend controls** — per-account monthly spend limit, low-balance alert, and auto top-up, enforced on registrations/renewals/transfers.
- **Per-key restrictions** — each API key can be scoped to specific source IPs (with CIDR support) and/or specific target domains (`IP_NOT_ALLOWED` / `DOMAIN_NOT_ALLOWED`).
- **DNS record types** — `HTTPS`, `SVCB`, and `SSHFP` documented as supported types.
- **Machine-readable error codes** — every error response includes a `code` field.

---

# Endpoints

## POST /api/json/v3/ping

**Test credentials and get caller IP**

Returns the caller's public IP address. Optionally validates API credentials.

- **No credentials supplied** — returns IP only.
- **Valid credentials supplied** — returns IP with `credentialsValid: true`.
- **Invalid credentials supplied** — returns an error.

Useful for agents and clients to verify their API key is working before making other calls.

```bash
curl -X POST https://api.porkbun.com/api/json/v3/ping \
  -H 'Content-Type: application/json' \
  -d '[]'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `yourIp` | string | The caller's public IP address |
| `xForwardedFor` | string | Raw value of the X-Forwarded-For header |
| `credentialsValid` | boolean | Present and true when valid credentials were supplied |

## GET /api/json/v3/ping

**Test credentials and get caller IP**

Returns the caller's public IP address. Optionally validates API credentials.

- **No credentials supplied** — returns IP only.
- **Valid credentials supplied** — returns IP with `credentialsValid: true`.
- **Invalid credentials supplied** — returns an error.

Useful for agents and clients to verify their API key is working before making other calls.

| Parameter | In | Required | Description |
|---|---|---|---|
| `Authorization` | header | no | Bearer token: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/ping'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `yourIp` | string | The caller's public IP address |
| `xForwardedFor` | string | Raw value of the X-Forwarded-For header |
| `credentialsValid` | boolean | Present and true when valid credentials were supplied |

## POST /api/json/v3/ip

**Get caller IP address**

Returns the caller's public IP address. No credentials required. Use the `api-ipv4.porkbun.com` hostname if you need to force an IPv4 address.

```bash
curl -X POST https://api.porkbun.com/api/json/v3/ip \
  -H 'Content-Type: application/json' \
  -d '[]'
```

Response fields (IpResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `yourIp` | string | The caller's public IP address |
| `xForwardedFor` | string | Raw value of the X-Forwarded-For header |

## GET /api/json/v3/ip

**Get caller IP address**

Returns the caller's public IP address. No credentials required. Use the `api-ipv4.porkbun.com` hostname if you need to force an IPv4 address.

```bash
curl 'https://api.porkbun.com/api/json/v3/ip'
```

Response fields (IpResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `yourIp` | string | The caller's public IP address |
| `xForwardedFor` | string | Raw value of the X-Forwarded-For header |

## POST /api/json/v3/pricing/get

**Retrieve domain pricing (public)**

Retrieve default domain pricing information for all supported TLDs. Does not require authentication. Prices are in US dollars.

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `tlds` | string[] | no | Optional array of TLDs to filter results. If omitted, all supported TLDs are returned. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/pricing/get \
  -H 'Content-Type: application/json' \
  -d '{"tlds":[]}'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `pricing` | object | Object keyed by TLD string |

## GET /api/json/v3/pricing/get

**Retrieve domain pricing (public)**

Retrieve default domain pricing information for all supported TLDs. Does not require authentication. Prices are in US dollars.

This GET form returns pricing for all TLDs. To filter by specific TLDs, use `POST /pricing/get` with a `tlds` array in the request body.

```bash
curl 'https://api.porkbun.com/api/json/v3/pricing/get'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `pricing` | object | Object keyed by TLD string |

## POST /api/json/v3/apikey/request

**Initiate an API key authorization request**

Initiate an API key authorization flow. No credentials are required. Returns a `requestToken` and `authUrl` that the account holder must visit to approve the request. After approval, the application should poll `/apikey/retrieve` to get the public API key. The secret API key is shown only in the user's browser and must be pasted into the application manually.

**Rate limit:** 20 requests per IP per 3600 seconds.

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | no | Optional human-readable name for the application requesting access. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/apikey/request \
  -H 'Content-Type: application/json' \
  -d '[]'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `requestToken` | string | Token used to poll `/apikey/retrieve` to check approval status. |
| `authUrl` | string | URL the account holder must visit to approve the request |
| `expiration` | string | ISO datetime when this request expires (10 minutes from creation) |
| `message` | string | Human-readable instructions |

## POST /api/json/v3/apikey/retrieve

**Poll for API key approval**

Poll to check whether the account holder has approved an API key authorization request. Returns `status: PENDING` while awaiting approval. On approval, returns the public API key. The secret API key is never transmitted via this endpoint — it is displayed in the user's browser and must be pasted into the application.

**Rate limit:** 120 requests per IP per 3600 seconds.

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `requestToken` | string | yes | The token returned by /apikey/request. Must be a 64-character lowercase hex string. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/apikey/retrieve \
  -H 'Content-Type: application/json' \
  -d '[]'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `apikey` | string | The approved public API key. Present only when status is SUCCESS. |
| `message` | string |  |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

## POST /api/json/v3/domain/checkDomain/{domain}

**Check domain availability**

Check if a domain is available for registration and retrieve current pricing. Includes registration, renewal, and transfer prices.

**Rate limit:** Configurable per API key. Default is 1 check per 10 seconds per account. Rate limit usage is returned in the `limits` field of the response.

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/checkDomain/{domain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields (CheckDomainResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `response` | object |  |
| `limits` | object | Current rate limit usage for this account |
| `ttlRemaining` | integer | Seconds remaining until the rate limit window resets |

## POST /api/json/v3/domain/create/{domain}

**Register a domain**

Register a domain using account credit. Requirements:
- Account email and phone must be verified
- Account must have sufficient credit
- `agreeToTerms` must be `'yes'` or `'1'`
- `cost` must equal the current price for the domain's minimum registration duration (in pennies)
- Account must have placed at least one previous domain registration
- Premium domains cannot be registered via API

Registrations are always for the registry-minimum duration (usually 1 year).

**WHOIS privacy.** WHOIS privacy is automatically enabled on new registrations (when the TLD supports it). Pass the optional `whoisPrivacy` field to override this on a per-registration basis, or change the account-level default under Account Security Settings on porkbun.com/account.

**Rate limits (both apply):**
- Attempt limit (default: 1 attempt per 10 seconds per account)
- Success limit (default: 50 successful registrations per 86400 seconds per account)

Both limits are configurable per API key and their current values are returned in the `limits` field of the response.

## Dry run

Add `dryRun: true` to validate everything and preview the cost WITHOUT registering or charging — nothing is created. Example response:

```json
{
  "status": "SUCCESS",
  "dryRun": true,
  "wouldSucceed": true,
  "operation": "registration",
  "domain": "example.com",
  "tld": "com",
  "available": "available",
  "premium": false,
  "duration": 1,
  "cost": 973,
  "costDisplay": "$9.73",
  "balance": 5000,
  "sufficientFunds": true,
  "message": "Dry run: this registration would succeed and cost $9.73. No order was created and no charge was made.",
  "requestId": "019e04fa-258d-7d11-aa86-4d5795c3fe8f"
}
```

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `cost` | integer | yes | The registration cost in pennies (USD cents). Must exactly equal the total price for the domain at its minimum registration duration. Obtain this from /domain/checkDomain first. |
| `agreeToTerms` | string | yes | Must be 'yes' or '1' to confirm agreement to the Domain Name Registration Agreement, Product Terms of Service, Privacy Policy, and automatic renewal terms. |
| `whoisPrivacy` | boolean | no | Optional. Override WHOIS privacy for this registration. When omitted, the account-level default is used (set under Account Security Settings on porkbun.com/account — defaults to enabled). Pass `true` to force-enable privacy or `false` to register with public contact info. Strings `"on"`/`"off"`, `"true"`/`"false"`, `"yes"`/`"no"`, `"1"`/`"0"` are also accepted. Has no effect on TLDs that don't support WHOIS privacy (privacy stays off regardless). |
| `dryRun` | boolean | no | Optional. When true, runs all pre-flight validation (availability, pricing, cost match, eligibility, funds, spend limit) and returns a preview with `dryRun: true` and `wouldSucceed` WITHOUT creating an order or charging. Nothing is registered and the rate-limit budget is not consumed. Use it to safely confirm an operation before committing. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/create/{domain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","cost":0,"agreeToTerms":"yes"}'
```

Response fields (CreateDomainResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `domain` | string | The registered domain name |
| `cost` | integer | The total amount charged in pennies |
| `orderId` | integer | Internal Porkbun order ID |
| `limits` | object | Current rate limit state for both attempt and success limits |
| `balance` | integer | Remaining account credit balance in pennies after the charge |
| `ttlRemaining` | integer | Seconds until the success rate limit window resets |
| `requestId` | string | Per-request UUID (also in the X-Request-Id header). Present on every API response. |

Response fields (DryRunPreviewResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `dryRun` | boolean | Always true on a dry-run preview — distinguishes it from a real success response. |
| `wouldSucceed` | boolean | True if the operation would complete given current funds and spend-limit state. (Hard validation failures — unavailable, bad price, ineligible — return a normal error response instead of a preview.) |
| `operation` | string |  |
| `domain` | string |  |
| `tld` | string |  |
| `available` | string | Availability as reported by the registry check (e.g. `available` / `unavailable`). |
| `premium` | boolean | Whether the domain is a premium/aftermarket name. |
| `duration` | integer | Term in years that would be purchased. |
| `cost` | integer | Total cost in pennies that would be charged. |
| `costDisplay` | string | Human-readable cost. |
| `balance` | integer | Current account credit balance in pennies. |
| `sufficientFunds` | boolean | Whether the balance covers the cost. |
| `monthlySpendLimit` | integer | The account's monthly API spend cap in pennies. Present only if a cap is configured. |
| `monthlySpendSoFar` | integer | API spend so far this calendar month in pennies. Present only if a cap is configured. |
| `withinMonthlySpendLimit` | boolean | Whether this cost stays within the monthly cap. Present only if a cap is configured. |
| `message` | string | Human-readable summary of the preview outcome. |
| `requestId` | string | Per-request UUID (also in the X-Request-Id header). Present on every API response. |

## POST /api/json/v3/domain/renew/{domain}

**Renew a domain**

Renew a domain using account credit. Requirements:
- Domain must be in your account and active
- Domain must be opted in to API access
- Account email and phone must be verified
- Account must have sufficient credit
- `cost` must equal the current renewal price for the domain's minimum renewal duration (in pennies)
- Domain must have been registered more than 30 days ago (checked against the domain's creation date)
- Domain must not have been successfully renewed within the last 30 days
- Premium renewals are not currently supported via API

Renewals are always for the registry-minimum duration (usually 1 year). Use `/domain/checkDomain/{domain}` with `priceType=renewal` to get the current price before renewing.

**Rate limits (both apply):**
- Attempt limit (default: 1 attempt per 10 seconds per account)
- Success limit (default: 50 successful renewals per 86400 seconds per account)

Both limits are configurable per API key and their current values are returned in the `limits` field of the response.

## Dry run

Add `dryRun: true` to validate everything and preview the cost WITHOUT renewing or charging — nothing is created. Example response:

```json
{
  "status": "SUCCESS",
  "dryRun": true,
  "wouldSucceed": true,
  "operation": "renewal",
  "domain": "example.com",
  "tld": "com",
  "available": "unavailable",
  "premium": false,
  "duration": 1,
  "cost": 1099,
  "costDisplay": "$10.99",
  "balance": 5000,
  "sufficientFunds": true,
  "message": "Dry run: this renewal would succeed and cost $10.99. No order was created and no charge was made.",
  "requestId": "019e04fa-3c11-7a02-9bd2-1f7c0e4a8b55"
}
```

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `cost` | integer | yes | The renewal cost in pennies (USD cents). Must exactly equal the total price for the domain at its minimum renewal duration. Obtain this from /domain/checkDomain first. |
| `dryRun` | boolean | no | Optional. When true, runs all pre-flight validation and returns a preview with `dryRun: true` and `wouldSucceed` WITHOUT renewing or charging. Nothing changes and the rate-limit budget is not consumed. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/renew/{domain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","cost":0}'
```

Response fields (RenewDomainResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `domain` | string | The renewed domain name |
| `expirationDate` | string | The new expiration date returned by the registry |
| `cost` | integer | The total amount charged in pennies |
| `orderId` | integer | Internal Porkbun order ID |
| `limits` | object | Current rate limit state for both attempt and success limits |
| `balance` | integer | Remaining account credit balance in pennies after the charge |
| `ttlRemaining` | integer | Seconds until the success rate limit window resets |
| `requestId` | string | Per-request UUID (also in the X-Request-Id header). Present on every API response. |

Response fields (DryRunPreviewResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `dryRun` | boolean | Always true on a dry-run preview — distinguishes it from a real success response. |
| `wouldSucceed` | boolean | True if the operation would complete given current funds and spend-limit state. (Hard validation failures — unavailable, bad price, ineligible — return a normal error response instead of a preview.) |
| `operation` | string |  |
| `domain` | string |  |
| `tld` | string |  |
| `available` | string | Availability as reported by the registry check (e.g. `available` / `unavailable`). |
| `premium` | boolean | Whether the domain is a premium/aftermarket name. |
| `duration` | integer | Term in years that would be purchased. |
| `cost` | integer | Total cost in pennies that would be charged. |
| `costDisplay` | string | Human-readable cost. |
| `balance` | integer | Current account credit balance in pennies. |
| `sufficientFunds` | boolean | Whether the balance covers the cost. |
| `monthlySpendLimit` | integer | The account's monthly API spend cap in pennies. Present only if a cap is configured. |
| `monthlySpendSoFar` | integer | API spend so far this calendar month in pennies. Present only if a cap is configured. |
| `withinMonthlySpendLimit` | boolean | Whether this cost stays within the monthly cap. Present only if a cap is configured. |
| `message` | string | Human-readable summary of the preview outcome. |
| `requestId` | string | Per-request UUID (also in the X-Request-Id header). Present on every API response. |

## POST /api/json/v3/domain/transfer/{domain}

**Initiate a domain transfer**

Initiates an inbound domain transfer to Porkbun using account credit. The transfer is processed asynchronously and typically takes 5–7 days to complete.

**Requirements:**
- Account email and phone must be verified.
- Sufficient account credit to cover the transfer cost.
- Domain must not already be in your account.
- No other active transfer for the same domain.
- `.uk` and manage-only TLDs are not supported via API.
- Premium domain transfers are not supported via API.

## Dry run

Add `dryRun: true` to validate everything and preview the cost WITHOUT initiating the transfer or charging — nothing is created. Example response:

```json
{
  "status": "SUCCESS",
  "dryRun": true,
  "wouldSucceed": true,
  "operation": "transfer",
  "domain": "example.com",
  "tld": "com",
  "available": "unavailable",
  "premium": false,
  "duration": 1,
  "cost": 999,
  "costDisplay": "$9.99",
  "balance": 5000,
  "sufficientFunds": true,
  "message": "Dry run: this transfer would succeed and cost $9.99. No order was created and no charge was made.",
  "requestId": "019e04fa-5f22-7c93-8a41-2e9d0b3f6c77"
}
```

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes | The domain name to transfer (e.g. `example.com`). |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `authCode` | string | yes | The EPP auth code for the domain. |
| `cost` | integer | yes | The transfer cost in cents as returned by the pricing API. Must match exactly. |
| `dryRun` | boolean | no | Optional. When true, runs all pre-flight validation and returns a preview with `dryRun: true` and `wouldSucceed` WITHOUT initiating the transfer or charging. Nothing changes and the rate-limit budget is not consumed. |

```bash
curl 'https://api.porkbun.com/api/json/v3/domain/transfer/example.com' \
  -H 'Content-Type: application/json' \
  -d '{
    "apikey": "pk1_...",
    "secretapikey": "sk1_...",
    "authCode": "abc123",
    "cost": 899
  }'
```

Response fields (TransferDomainResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `domain` | string |  |
| `orderId` | integer |  |
| `transferId` | integer |  |
| `message` | string |  |
| `balance` | integer | Remaining account credit balance in cents. |
| `ttlRemaining` | integer |  |
| `limits` | object |  |
| `requestId` | string | Per-request UUID (also in the X-Request-Id header). Present on every API response. |

Response fields (DryRunPreviewResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `dryRun` | boolean | Always true on a dry-run preview — distinguishes it from a real success response. |
| `wouldSucceed` | boolean | True if the operation would complete given current funds and spend-limit state. (Hard validation failures — unavailable, bad price, ineligible — return a normal error response instead of a preview.) |
| `operation` | string |  |
| `domain` | string |  |
| `tld` | string |  |
| `available` | string | Availability as reported by the registry check (e.g. `available` / `unavailable`). |
| `premium` | boolean | Whether the domain is a premium/aftermarket name. |
| `duration` | integer | Term in years that would be purchased. |
| `cost` | integer | Total cost in pennies that would be charged. |
| `costDisplay` | string | Human-readable cost. |
| `balance` | integer | Current account credit balance in pennies. |
| `sufficientFunds` | boolean | Whether the balance covers the cost. |
| `monthlySpendLimit` | integer | The account's monthly API spend cap in pennies. Present only if a cap is configured. |
| `monthlySpendSoFar` | integer | API spend so far this calendar month in pennies. Present only if a cap is configured. |
| `withinMonthlySpendLimit` | boolean | Whether this cost stays within the monthly cap. Present only if a cap is configured. |
| `message` | string | Human-readable summary of the preview outcome. |
| `requestId` | string | Per-request UUID (also in the X-Request-Id header). Present on every API response. |

## GET /api/json/v3/domain/getTransfer/{domain}

**Get transfer status**

Returns the most recent transfer record for the specified domain in your account. Authenticate using `X-API-Key` and `X-Secret-API-Key` headers, or `Authorization: Bearer <token>`.

| Parameter | In | Required | Description |
|---|---|---|---|
| `Authorization` | header | no | Bearer token: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |
| `domain` | path | yes | The domain name (e.g. `example.com`). |

```bash
curl 'https://api.porkbun.com/api/json/v3/domain/getTransfer/example.com' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields (GetTransferResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `transfer` | object |  |

## GET /api/json/v3/domain/listTransfers

**List active transfers**

Returns all active inbound domain transfers for your account (excludes completed and canceled transfers). Authenticate using `X-API-Key` and `X-Secret-API-Key` headers, or `Authorization: Bearer <token>`.

| Parameter | In | Required | Description |
|---|---|---|---|
| `Authorization` | header | no | Bearer token: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/domain/listTransfers' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields (ListTransfersResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `transfers` | object[] |  |

## POST /api/json/v3/domain/listAll

**List all domains**

Retrieve domains in the authenticated account. Results are returned in chunks of up to 1000 domains. Use `start` to paginate.

**Filtering:** all filter parameters are optional. Combine them freely.

- `domain` — exact match (returns 0 or 1)
- `nameContains` — substring search
- `tlds` — limit to these TLDs
- `expiringWithinDays` — only domains expiring within N days
- `autoRenew` — `yes` / `no`
- `apiAccess` — `yes` / `no` (filter to domains the API key can operate on)
- `sortName` — `domain` / `tld` / `create_date` / `expire_date`
- `sortDirection` — `asc` / `desc`

Supports both GET (with header auth) and POST (with body or header auth). For multi-value `tlds` on GET, use bracket syntax: `?tlds[]=com&tlds[]=io`.

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `start` | integer | no | Zero-based offset for pagination (default: 0). Returns up to 1000 domains per call. |
| `includeLabels` | string | no | Return label metadata for each domain. Defaults to no. |
| `domain` | string | no | Exact domain name match. Returns 0 or 1 result. Useful as an alternative to `/domain/get/{domain}`. |
| `nameContains` | string | no | Substring match against the full domain name. Case-insensitive. |
| `expiringWithinDays` | integer | no | Filter to domains expiring within this many days (relative to now). |
| `tlds` | string[] | no | Limit to these TLDs (without leading dot). |
| `autoRenew` | string | no | Filter to domains with auto-renew on or off. |
| `apiAccess` | string | no | Filter to domains opted in to API access (yes) or not (no). Useful for finding domains an API key can actually operate on. |
| `sortName` | string | no | Field to sort by. Default: `expire_date` ascending. |
| `sortDirection` | string | no | Sort direction. Default: asc. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/listAll \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","start":0,"includeLabels":"yes"}'
```

Response fields (DomainListAllResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `count` | integer | Number of domains returned in this page. |
| `domains` | object[] |  |

## GET /api/json/v3/domain/listAll

**List all domains**

Retrieve domains in the authenticated account. Results are returned in chunks of up to 1000 domains. Use `start` to paginate.

**Filtering:** all filter parameters are optional. Combine them freely.

- `domain` — exact match (returns 0 or 1)
- `nameContains` — substring search
- `tlds` — limit to these TLDs
- `expiringWithinDays` — only domains expiring within N days
- `autoRenew` — `yes` / `no`
- `apiAccess` — `yes` / `no` (filter to domains the API key can operate on)
- `sortName` — `domain` / `tld` / `create_date` / `expire_date`
- `sortDirection` — `asc` / `desc`

Supports both GET (with header auth) and POST (with body or header auth). For multi-value `tlds` on GET, use bracket syntax: `?tlds[]=com&tlds[]=io`.

| Parameter | In | Required | Description |
|---|---|---|---|
| `Authorization` | header | no | Bearer token: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |
| `start` | query | no | Zero-based offset for pagination. Returns up to 1000 domains per call. |
| `includeLabels` | query | no | Return label metadata for each domain. Defaults to no. |
| `domain` | query | no | Exact domain name match. Returns 0 or 1 result. |
| `nameContains` | query | no | Substring match against the full domain name. Case-insensitive. |
| `expiringWithinDays` | query | no | Filter to domains expiring within this many days. |
| `tlds` | query | no | Limit to these TLDs (no leading dot). Use bracket form: `?tlds[]=com&tlds[]=io`. |
| `autoRenew` | query | no | Filter to domains with auto-renew on or off. |
| `apiAccess` | query | no | Filter to domains opted in to API access. |
| `sortName` | query | no | Field to sort by. Default: `expire_date` ascending. |
| `sortDirection` | query | no | Sort direction. Default: asc. |

```bash
curl 'https://api.porkbun.com/api/json/v3/domain/listAll?tlds[]=com&expiringWithinDays=30&autoRenew=no' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields (DomainListAllResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `count` | integer | Number of domains returned in this page. |
| `domains` | object[] |  |

## GET /api/json/v3/domain/get/{domain}

**Get a single domain**

Get the metadata for a single domain in the authenticated account. Returns the same per-domain shape as `listAll` items but as a single object. Returns HTTP 404 with code `DOMAIN_NOT_FOUND` if the domain isn't in the account.

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes | Fully qualified domain name in the authenticated account. |
| `Authorization` | header | no | Bearer token: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |
| `includeLabels` | query | no | Return label metadata. Defaults to no. |

```bash
curl 'https://api.porkbun.com/api/json/v3/domain/get/example.com' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `domain` | object |  |

## POST /api/json/v3/domain/updateAutoRenew/{domain}

**Update auto-renew setting**

Update the auto-renew setting for one or more domains. The domain can be passed in the URL path or in the `domains` array in the request body (or both). Both are combined and deduplicated.

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | no | Optional single domain in URL. Omit or use `/domain/updateAutoRenew/` (without trailing domain) when using the `domains` body array instead. |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `status` | string | yes | Auto-renew status to set |
| `domains` | string[] | no | Array of additional domain names to update. Combined with the domain in the URL path if provided. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/updateAutoRenew/{domain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","status":"on","domains":[]}'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `results` | object | Object keyed by domain name |

## POST /api/json/v3/domain/getNs/{domain}

**Get nameservers**

Retrieve the authoritative nameservers listed at the registry for the domain. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/getNs/{domain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `ns` | string[] |  |

## GET /api/json/v3/domain/getNs/{domain}

**Get nameservers**

Retrieve the authoritative nameservers listed at the registry for the domain. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `Authorization` | header | no | Bearer token auth: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/domain/getNs/{domain}' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `ns` | string[] |  |

## POST /api/json/v3/domain/updateNs/{domain}

**Update nameservers**

Update the nameservers for the domain at the registry.

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `ns` | string[] | yes | Ordered array of nameserver hostnames |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/updateNs/{domain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","ns":["ns1.example.com","ns2.example.com"]}'
```

Response fields (BasicResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `message` | string | Human-readable message. Present on ERROR, sometimes on SUCCESS. |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

## POST /api/json/v3/domain/getGlue/{domain}

**Get glue records**

Retrieve all glue records (host objects) registered under the domain. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/getGlue/{domain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `hosts` | array[] | Array of [hostname, ipAddresses] tuples. Each element is a two-item array: index 0 is the full hostname (string), index 1 is an object with `v4` (array of IPv4 strings) and `v6` (array of IPv6 strings). Example: `["ns1.example.com", {"v4": ["1.2.3.4"], "v6": []}]` |

## GET /api/json/v3/domain/getGlue/{domain}

**Get glue records**

Retrieve all glue records (host objects) registered under the domain. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `Authorization` | header | no | Bearer token auth: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/domain/getGlue/{domain}' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `hosts` | array[] | Array of [hostname, ipAddresses] tuples. Each element is a two-item array: index 0 is the full hostname (string), index 1 is an object with `v4` (array of IPv4 strings) and `v6` (array of IPv6 strings). Example: `["ns1.example.com", {"v4": ["1.2.3.4"], "v6": []}]` |

## POST /api/json/v3/domain/createGlue/{domain}/{subdomain}

**Create glue record**

Create a glue record (host object) for a nameserver hostname under the domain. Use this when you want to host a nameserver at a subdomain of the domain itself (e.g. ns1.example.com).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `subdomain` | path | yes | The subdomain portion only (e.g. 'ns1' for ns1.example.com) |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `ips` | string[] | yes | Array of IP addresses (IPv4 and/or IPv6) to associate with the host record |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/createGlue/{domain}/{subdomain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","ips":["1.2.3.4","2001:db8::1"]}'
```

Response fields (BasicResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `message` | string | Human-readable message. Present on ERROR, sometimes on SUCCESS. |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

## POST /api/json/v3/domain/updateGlue/{domain}/{subdomain}

**Update glue record**

Update the IP addresses of a glue record. All existing IP addresses are replaced with the supplied list.

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `subdomain` | path | yes |  |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `ips` | string[] | yes | Array of IP addresses (IPv4 and/or IPv6) to associate with the host record |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/updateGlue/{domain}/{subdomain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","ips":["1.2.3.4","2001:db8::1"]}'
```

Response fields (BasicResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `message` | string | Human-readable message. Present on ERROR, sometimes on SUCCESS. |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

## POST /api/json/v3/domain/deleteGlue/{domain}/{subdomain}

**Delete glue record**

Delete a glue record (host object) for a subdomain.

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `subdomain` | path | yes |  |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/deleteGlue/{domain}/{subdomain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields (BasicResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `message` | string | Human-readable message. Present on ERROR, sometimes on SUCCESS. |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

## POST /api/json/v3/domain/getUrlForwarding/{domain}

**List URL forwards**

Retrieve all active URL forwards for a domain. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/getUrlForwarding/{domain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields (GetUrlForwardingResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `forwards` | object[] |  |

## GET /api/json/v3/domain/getUrlForwarding/{domain}

**List URL forwards**

Retrieve all active URL forwards for a domain. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `Authorization` | header | no | Bearer token auth: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/domain/getUrlForwarding/{domain}' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields (GetUrlForwardingResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `forwards` | object[] |  |

## POST /api/json/v3/domain/addUrlForward/{domain}

**Add URL forward**

Add a URL forward for a domain or subdomain.

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `subdomain` | string | no | Subdomain to forward (optional, leave blank or omit for root domain). Alphanumeric and hyphens only. |
| `location` | string | yes | Destination URL to forward to |
| `type` | string | yes | 'permanent' sends HTTP 301, 'temporary' sends the configured default redirect code |
| `includePath` | string | yes | Whether to append the request URI path to the forwarding destination |
| `wildcard` | string | yes | Whether to also forward all subdomains of the forwarded subdomain |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/addUrlForward/{domain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","subdomain":"www","location":"https:\/\/destination.example.com","type":"temporary","includePath":"yes","wildcard":"yes"}'
```

Response fields (BasicResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `message` | string | Human-readable message. Present on ERROR, sometimes on SUCCESS. |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

## POST /api/json/v3/domain/deleteUrlForward/{domain}/{id}

**Delete URL forward**

Delete a specific URL forward by ID.

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `id` | path | yes | URL forward record ID |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/deleteUrlForward/{domain}/{id} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields (BasicResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `message` | string | Human-readable message. Present on ERROR, sometimes on SUCCESS. |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

## POST /api/json/v3/dns/retrieve/{domain}

**Retrieve all DNS records**

Retrieve all editable DNS records for a domain. SOA records and Porkbun default NS records are excluded. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/dns/retrieve/{domain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields (DnsRecordsResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `cloudflare` | string | Whether Cloudflare proxy is enabled for this domain |
| `records` | object[] |  |

## GET /api/json/v3/dns/retrieve/{domain}

**Retrieve all DNS records**

Retrieve all editable DNS records for a domain. SOA records and Porkbun default NS records are excluded. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `Authorization` | header | no | Bearer token auth: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/dns/retrieve/{domain}' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields (DnsRecordsResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `cloudflare` | string | Whether Cloudflare proxy is enabled for this domain |
| `records` | object[] |  |

## POST /api/json/v3/dns/retrieve/{domain}/{id}

**Retrieve DNS record by ID**

Retrieve a specific DNS record by its numeric ID. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `id` | path | yes | Numeric DNS record ID |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/dns/retrieve/{domain}/{id} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields (DnsRecordsResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `cloudflare` | string | Whether Cloudflare proxy is enabled for this domain |
| `records` | object[] |  |

## GET /api/json/v3/dns/retrieve/{domain}/{id}

**Retrieve DNS record by ID**

Retrieve a specific DNS record by its numeric ID. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `id` | path | yes | Numeric DNS record ID |
| `Authorization` | header | no | Bearer token auth: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/dns/retrieve/{domain}/{id}' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields (DnsRecordsResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `cloudflare` | string | Whether Cloudflare proxy is enabled for this domain |
| `records` | object[] |  |

## POST /api/json/v3/dns/retrieveByNameType/{domain}/{type}/{subdomain}

**Retrieve DNS records by name and type**

Retrieve all DNS records for a domain that match a specific subdomain and record type. Omit `subdomain` (or leave the path segment empty) to query the root domain. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `type` | path | yes | DNS record type (A, AAAA, CNAME, MX, TXT, etc.) |
| `subdomain` | path | no | Subdomain portion only. Omit or leave empty for root domain records. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/dns/retrieveByNameType/{domain}/{type}/{subdomain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields (DnsRecordsResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `cloudflare` | string | Whether Cloudflare proxy is enabled for this domain |
| `records` | object[] |  |

## GET /api/json/v3/dns/retrieveByNameType/{domain}/{type}/{subdomain}

**Retrieve DNS records by name and type**

Retrieve all DNS records for a domain that match a specific subdomain and record type. Omit `subdomain` (or leave the path segment empty) to query the root domain. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `type` | path | yes | DNS record type (A, AAAA, CNAME, MX, TXT, etc.) |
| `subdomain` | path | no | Subdomain portion only. Omit or leave empty for root domain records. |
| `Authorization` | header | no | Bearer token auth: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/dns/retrieveByNameType/{domain}/{type}/{subdomain}' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields (DnsRecordsResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `cloudflare` | string | Whether Cloudflare proxy is enabled for this domain |
| `records` | object[] |  |

## POST /api/json/v3/dns/create/{domain}

**Create DNS record**

Create a new DNS record for a domain. The record ID is returned in the response.

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | no | Subdomain for the record (e.g. 'www', '*' for wildcard, blank for root). Do not include the domain name itself. |
| `type` | string | yes | DNS record type |
| `content` | string | yes | The record value |
| `ttl` | integer | no | Time to live in seconds. Minimum is determined by account settings (typically 600). Defaults to the account minimum if omitted or 0. |
| `prio` | integer | no | Priority for MX and SRV records. Defaults to 0 if omitted. |
| `notes` | string | no | Optional notes to store with the record (not served in DNS) |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/dns/create/{domain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","name":"www","type":"A","content":"1.2.3.4","ttl":600,"prio":10}'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `id` | string | The numeric ID of the newly created record |

## POST /api/json/v3/dns/edit/{domain}/{id}

**Edit DNS record by ID**

Edit a specific DNS record by its numeric ID. SOA and default Porkbun NS records cannot be edited.

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `id` | path | yes | Numeric DNS record ID |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | no | Subdomain for the record. Do not include the domain name itself. |
| `type` | string | yes | DNS record type |
| `content` | string | yes | The record value |
| `ttl` | integer | no | Time to live in seconds (optional) |
| `prio` | integer | no | Priority for MX/SRV records (optional) |
| `notes` | string | no | Notes (optional). Pass empty string to clear notes; omit or pass null to leave unchanged. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/dns/edit/{domain}/{id} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","type":"A","content":"5.6.7.8","ttl":0,"prio":0}'
```

Response fields (BasicResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `message` | string | Human-readable message. Present on ERROR, sometimes on SUCCESS. |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

## POST /api/json/v3/dns/editByNameType/{domain}/{type}/{subdomain}

**Edit DNS records by name and type**

Replace the content of all records matching the given subdomain and type. SOA and NS records cannot be edited with this method (use edit by ID instead).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `type` | path | yes |  |
| `subdomain` | path | no | Subdomain portion only. Omit or leave empty for root domain records. |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `content` | string | yes | New record value to set on all matching records |
| `ttl` | integer | no | Time to live in seconds (optional) |
| `prio` | integer | no | Priority (optional) |
| `notes` | string | no | Notes to store with the record (not served in DNS). Pass an empty string to clear existing notes; pass null or omit this field to leave notes unchanged. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/dns/editByNameType/{domain}/{type}/{subdomain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","content":"5.6.7.8","ttl":0,"prio":0}'
```

Response fields (BasicResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `message` | string | Human-readable message. Present on ERROR, sometimes on SUCCESS. |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

## POST /api/json/v3/dns/delete/{domain}/{id}

**Delete DNS record by ID**

Delete a specific DNS record. SOA and default Porkbun NS records cannot be deleted.

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `id` | path | yes | Numeric DNS record ID |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/dns/delete/{domain}/{id} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields (BasicResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `message` | string | Human-readable message. Present on ERROR, sometimes on SUCCESS. |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

## POST /api/json/v3/dns/deleteByNameType/{domain}/{type}/{subdomain}

**Delete DNS records by name and type**

Delete all DNS records matching the given subdomain and type. SOA and NS records cannot be deleted with this method (use delete by ID instead).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `type` | path | yes |  |
| `subdomain` | path | no | Subdomain portion only. Omit or leave empty for root domain records. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/dns/deleteByNameType/{domain}/{type}/{subdomain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields (BasicResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `message` | string | Human-readable message. Present on ERROR, sometimes on SUCCESS. |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

## POST /api/json/v3/dns/getDnssecRecords/{domain}

**Get DNSSEC records**

Retrieve DNSSEC records associated with the domain at the registry. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/dns/getDnssecRecords/{domain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `records` | object | Object keyed by key tag value. Each value is an object containing the DNSSEC data fields. |

## GET /api/json/v3/dns/getDnssecRecords/{domain}

**Get DNSSEC records**

Retrieve DNSSEC records associated with the domain at the registry. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `Authorization` | header | no | Bearer token auth: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/dns/getDnssecRecords/{domain}' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `records` | object | Object keyed by key tag value. Each value is an object containing the DNSSEC data fields. |

## POST /api/json/v3/dns/createDnssecRecord/{domain}

**Create DNSSEC record**

Create a DNSSEC DS or key record at the registry. DNSSEC requirements vary by registry — `keyTag`, `alg`, `digestType`, and `digest` are the minimum required fields. Key data fields are optional and will be omitted if not accepted by the registry.

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `keyTag` | string | yes | DNSSEC key tag |
| `alg` | string | yes | DS Data algorithm number (e.g. 13 for ECDSA P-256 SHA-256) |
| `digestType` | string | yes | Digest type number (e.g. 2 for SHA-256) |
| `digest` | string | yes | Hex-encoded digest value |
| `maxSigLife` | string | no | Maximum signature lifetime in seconds (optional, registry-specific) |
| `keyDataFlags` | string | no | Key data flags (optional, used when submitting full key data) |
| `keyDataProtocol` | string | no | Key data protocol (optional) |
| `keyDataAlgo` | string | no | Key data algorithm (optional) |
| `keyDataPubKey` | string | no | Key data public key in base64 (optional) |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/dns/createDnssecRecord/{domain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","keyTag":"12345","alg":"13","digestType":"2","digest":"ABCD1234..."}'
```

Response fields (BasicResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `message` | string | Human-readable message. Present on ERROR, sometimes on SUCCESS. |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

## POST /api/json/v3/dns/deleteDnssecRecord/{domain}/{keytag}

**Delete DNSSEC record**

Delete a DNSSEC record from the registry by key tag. Note: most registries delete all records matching the key data, not only the record with the specified key tag.

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `keytag` | path | yes | The DNSSEC key tag value |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/dns/deleteDnssecRecord/{domain}/{keytag} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields (BasicResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `message` | string | Human-readable message. Present on ERROR, sometimes on SUCCESS. |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

## POST /api/json/v3/ssl/retrieve/{domain}

**Retrieve SSL bundle**

Retrieve the Let's Encrypt SSL certificate bundle for a domain. The certificate must already be issued (status HAVECERT). Token-based access is not supported for this endpoint. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/ssl/retrieve/{domain} \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `certificatechain` | string | The full PEM-encoded certificate chain (certificate + intermediates) |
| `privatekey` | string | The PEM-encoded private key |
| `publickey` | string | The PEM-encoded public key |

## GET /api/json/v3/ssl/retrieve/{domain}

**Retrieve SSL bundle**

Retrieve the Let's Encrypt SSL certificate bundle for a domain. The certificate must already be issued (status HAVECERT). Token-based access is not supported for this endpoint. Supports both GET (with header auth) and POST (with body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `domain` | path | yes |  |
| `Authorization` | header | no | Bearer token auth: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/ssl/retrieve/{domain}' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `certificatechain` | string | The full PEM-encoded certificate chain (certificate + intermediates) |
| `privatekey` | string | The PEM-encoded private key |
| `publickey` | string | The PEM-encoded public key |

## POST /api/json/v3/email/setPassword

**Set email hosting password**

Set the password for an email hosting account associated with a domain managed by your API key.

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `emailAddress` | string | yes | The full email address (e.g. user@example.com) |
| `password` | string | yes | The new password. Must pass Porkbun password validation rules. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/email/setPassword \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","emailAddress":"user@example.com"}'
```

Response fields (BasicResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `message` | string | Human-readable message. Present on ERROR, sometimes on SUCCESS. |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

## GET /api/json/v3/marketplace/getAll

**List marketplace domains**

GET form of `/marketplace/getAll` for read-friendly filtering and URL-shareable searches. Authenticate via `X-API-Key` and `X-Secret-API-Key` headers, or `Authorization: Bearer <token>`. All filter params are optional and mirror the POST body. For multi-value `tlds`, use bracket syntax: `?tlds[]=com&tlds[]=io`.

See the POST documentation for the full filtering semantics (unfiltered pagination vs filtered mode, `+include` / `-exclude` query prefixes, sort options).

| Parameter | In | Required | Description |
|---|---|---|---|
| `Authorization` | header | no | Bearer token: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |
| `query` | query | no | SLD substring search. Multi-word; prefix a term with `-` to exclude. Example: `ai -test`. |
| `tlds` | query | no | Filter to listings under these TLDs (without leading dot). Use bracket form: `?tlds[]=com&tlds[]=io`. |
| `sldLengthMin` | query | no | Minimum SLD character length. |
| `sldLengthMax` | query | no | Maximum SLD character length. |
| `sortName` | query | no | Field to sort filtered results by. |
| `sortDirection` | query | no | Sort direction. |
| `start` | query | no | Pagination offset (unfiltered mode only). |
| `limit` | query | no | Page size (unfiltered mode only). Default 1000, max 5000. |

```bash
curl 'https://api.porkbun.com/api/json/v3/marketplace/getAll?query=ai&tlds[]=com&tlds[]=io&sldLengthMax=6&sortName=price&sortDirection=asc' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `count` | integer |  |
| `filtered` | boolean |  |
| `domains` | object[] |  |

## POST /api/json/v3/marketplace/getAll

**List marketplace domains**

Retrieve domains listed on the Porkbun marketplace. Two modes:

- **Unfiltered (default):** paginated raw listing, up to 5000 entries per call via `start` / `limit`.
- **Filtered:** when any of `query`, `tlds`, `sldLengthMin`, `sldLengthMax`, or `sortName` is provided, results are filtered server-side. Filtered mode returns up to 1000 matching listings (matches the web UI's marketplace search).

`query` supports `+include` and `-exclude` prefixes per word against the SLD (e.g. `+ai -test` matches SLDs containing 'ai' but not 'test'). Token-based access is not supported.

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `start` | integer | no | Pagination offset (unfiltered mode only). Default 0. |
| `limit` | integer | no | Number of domains to return (unfiltered mode only). Default 1000, max 5000. |
| `query` | string | no | Search string. Each space-separated term filters by SLD substring. Prefix a term with `-` to exclude it. |
| `tlds` | string[] | no | Filter to listings under these TLDs (without the leading dot). |
| `sldLengthMin` | integer | no | Filter to listings whose SLD has at least this many characters. |
| `sldLengthMax` | integer | no | Filter to listings whose SLD has at most this many characters. |
| `sortName` | string | no | Field to sort filtered results by. Default `sld_length` ascending when `query` is set, otherwise `create_date` descending. |
| `sortDirection` | string | no | Sort direction. Defaults vary by `sortName`. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/marketplace/getAll \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","start":0,"limit":1000}'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `count` | integer | Number of domains returned in this response |
| `filtered` | boolean | True when one or more filter parameters were applied (query, tlds, sldLengthMin/Max, sortName). |
| `domains` | object[] |  |

## POST /api/json/v3/auth/login

**Login with username and password**

**Partner-only endpoint** (requires `auth:login` access on the API key). Authenticate a Porkbun account with username and password and receive a short-lived token (5 minutes). Supports TOTP and email-based 2FA. Returns HTTP 403 with a `2FA` field when a second factor is required.

| Parameter | In | Required | Description |
|---|---|---|---|
| `Sig` | header | yes | Base64-encoded SHA-256 digest of the trimmed request body, signed with the private key associated with the API key. |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `username` | string | yes |  |
| `password` | string | yes |  |
| `twoFactorCode` | string | no | TOTP or email 2FA code, if required |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `token` | string | Short-lived session token (5 minutes) |
| `expiration` | string |  |

## POST /api/json/v3/account/invite

**Create an account registration invite**

Generates a one-time invite token and URL that you send to a prospective user. When the user visits the URL they go through Porkbun's normal account creation flow — including CAPTCHA, address collection, and TOS acceptance — in the browser. The invite expires in 48 hours. Each token can only be used once.

Optionally supply an `email` to pre-fill the email field on the registration form.

Optionally supply a `returnUrl` (must be HTTPS) to redirect the user back to your platform after they complete registration.

After sending the invite URL, poll `/account/inviteStatus` to check whether the account was created.

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `email` | string | no | Email address to pre-fill on the registration form (optional) |
| `returnUrl` | string | no | HTTPS URL to redirect the user to after successful registration (optional). Use this to send users back to your platform after they complete account creation. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/account/invite \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","email":"newuser@example.com"}'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `inviteToken` | string | Opaque token — pass to `/account/inviteStatus` to track completion |
| `inviteUrl` | string | URL to send to the prospective user. Opens Porkbun's standard registration page. |
| `expires` | string | UTC datetime when the invite token expires (48 hours from creation) |

## GET /api/json/v3/account/inviteStatus

**Check account invite status**

Returns the current status of a registration invite created by your API key.

- `PENDING` — the invite URL has not yet been used
- `ACCEPTED` — the user completed registration; `newAccountId` contains their account ID
- `EXPIRED` — the invite was not used within 48 hours or was canceled

You can only query invites created by your own API key. Pass credentials via `X-API-Key` and `X-Secret-API-Key` headers.

| Parameter | In | Required | Description |
|---|---|---|---|
| `token` | query | yes | The `inviteToken` returned by `/account/invite` |
| `X-API-Key` | header | yes | Your API key |
| `X-Secret-API-Key` | header | yes | Your secret API key |

```bash
curl 'https://api.porkbun.com/api/json/v3/account/inviteStatus?token=a3f8c2...' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `inviteStatus` | string | Current state of the invite |
| `newAccountId` | integer | ID of the newly created account. Present only when `inviteStatus` is `ACCEPTED`. |

## GET /api/json/v3/account/balance

**Get account balance**

Returns the available account credit balance. Authenticate using `X-API-Key` and `X-Secret-API-Key` headers, or `Authorization: Bearer <token>`.

| Parameter | In | Required | Description |
|---|---|---|---|
| `Authorization` | header | no | Bearer token: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/account/balance' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields (BalanceResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `balance` | integer | Available account credit balance in cents. |
| `display` | string | Human-readable balance string (e.g. `$12.34`). |

## GET /api/json/v3/account/apiSettings

**Get API spend settings**

Returns the account's API spend control settings and current month's spend total. All amounts are in cents. Authenticate using `X-API-Key` and `X-Secret-API-Key` headers, or `Authorization: Bearer <token>`.

| Parameter | In | Required | Description |
|---|---|---|---|
| `Authorization` | header | no | Bearer token: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/account/apiSettings' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields (ApiSettingsResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `settings` | object |  |
| `monthlySpend` | integer | Total API spend in the current calendar month, in cents. |

## GET /api/json/v3/webhook/eventTypes

**List subscribable event types**

Return the catalog of event types a webhook endpoint can subscribe to. Read-only; supports GET (header auth) or POST (body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `Authorization` | header | no | Bearer token: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/webhook/eventTypes' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields (WebhookEventTypesResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `eventTypes` | string[] |  |

## GET /api/json/v3/webhook/list

**List webhook endpoints**

List all webhook endpoints registered on the authenticated account, including each endpoint's signing secret and delivery health. Read-only; supports GET (header auth) or POST (body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `Authorization` | header | no | Bearer token: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/webhook/list' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields (WebhookListResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `endpoints` | object[] |  |

## GET /api/json/v3/webhook/get/{id}

**Get a webhook endpoint**

Fetch a single webhook endpoint by id, including its signing secret and delivery health. Read-only; supports GET (header auth) or POST (body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `id` | path | yes |  |
| `Authorization` | header | no | Bearer token: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/webhook/get/42' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields (WebhookEndpointResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `endpoint` | object |  |

## POST /api/json/v3/webhook/create

**Create a webhook endpoint**

Register an HTTPS endpoint to receive signed event payloads. The response includes the generated `secret` — store it securely; it is the HMAC key used to verify the `X-Porkbun-Signature` header. Omit `events` (or pass `["*"]`) to subscribe to all event types. Maximum 20 endpoints per account.

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `url` | string | yes | HTTPS URL to deliver events to. |
| `events` | string[] | no | Event types to subscribe to. Omit, or pass ["*"], for all events. Prefix wildcards like "dns.*" are allowed. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/webhook/create \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","url":"https://example.com/porkbun/webhook","events":["*"]}'
```

Response fields (WebhookEndpointResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `endpoint` | object |  |

## POST /api/json/v3/webhook/update

**Update a webhook endpoint**

Update an endpoint's URL, event subscriptions, and/or status. Only the supplied fields change. Set `status` to `DISABLED` to pause deliveries or `ACTIVE` to resume (resuming also resets the consecutive-failure counter).

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `id` | integer | yes | Endpoint id to update. |
| `url` | string | no | New HTTPS URL (optional). |
| `events` | string[] | no | Replacement event subscription list (optional). |
| `status` | string | no | Set ACTIVE to resume (also clears the failure counter) or DISABLED to pause (optional). |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/webhook/update \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","id":42,"status":"DISABLED"}'
```

Response fields (WebhookEndpointResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `endpoint` | object |  |

## POST /api/json/v3/webhook/rotateSecret

**Rotate the signing secret**

Generate a new signing secret for an endpoint and return the endpoint with the new secret. Deliveries are signed with the new secret immediately, so update your verifier as part of the same operation.

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `id` | integer | yes | Endpoint id. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/webhook/rotateSecret \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","id":42}'
```

Response fields (WebhookEndpointResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `endpoint` | object |  |

## POST /api/json/v3/webhook/test

**Send a test event**

Enqueue a `webhook.test` event to the endpoint so you can confirm reachability and that your signature verification works. The endpoint must be ACTIVE. Delivery is asynchronous (usually within a minute).

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `id` | integer | yes | Endpoint id. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/webhook/test \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","id":42}'
```

Response fields (WebhookTestResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `eventId` | string | UUID of the queued webhook.test event. |
| `message` | string |  |

## POST /api/json/v3/webhook/delete

**Delete a webhook endpoint**

Delete a webhook endpoint by id. Deliveries stop immediately.

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `id` | integer | yes | Endpoint id. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/webhook/delete \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","id":42}'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `message` | string |  |

## GET /api/json/v3/webhook/deliveries

**List webhook deliveries**

List recent delivery attempts across the account (newest first), optionally filtered by endpoint or status. The bulky payload is omitted — use GET /webhook/delivery/{id} for it. History is retained about 30 days. Read-only; supports GET (header auth) or POST (body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `endpointId` | query | no | Only deliveries for this endpoint. |
| `status` | query | no | Filter by delivery status. |
| `start` | query | no | Pagination offset (default 0). |
| `limit` | query | no | Page size, 1-200 (default 50). |
| `Authorization` | header | no | Bearer token: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/webhook/deliveries?status=FAILED&limit=50' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields (WebhookDeliveryListResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `deliveries` | object[] | Newest first. The bulky `payload` field is omitted here. |
| `total` | integer | Total matching deliveries (for pagination). |
| `start` | integer | Offset of this page. |
| `limit` | integer | Page size used. |
| `message` | string |  |

## GET /api/json/v3/webhook/delivery/{id}

**Get a webhook delivery**

Fetch a single delivery including the full event payload that was (or will be) sent and its delivery status. Read-only; supports GET (header auth) or POST (body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `id` | path | yes |  |
| `Authorization` | header | no | Bearer token: `Authorization: Bearer <token>` |
| `X-API-Key` | header | no | API key header auth (use with X-Secret-API-Key) |
| `X-Secret-API-Key` | header | no | Secret API key header auth (use with X-API-Key) |

```bash
curl 'https://api.porkbun.com/api/json/v3/webhook/delivery/9001' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields (WebhookDeliveryResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `delivery` | object |  |

## POST /api/json/v3/webhook/resend

**Resend a delivery**

Re-queue a past delivery to its endpoint. Clones it into a fresh attempt reusing the ORIGINAL event id (so consumers that dedupe on X-Porkbun-Webhook-Id treat it as the same event). The endpoint must still exist and be ACTIVE. The original delivery row is left intact as history.

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `id` | integer | yes | Endpoint id. |

```bash
curl -X POST https://api.porkbun.com/api/json/v3/webhook/resend \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","id":9001}'
```

Response fields (WebhookResendResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `delivery` | object |  |
| `message` | string |  |

---

## More

- Topic index: https://porkbun.com/llms
- Full reference (one file): https://porkbun.com/llms-full.txt
- OpenAPI spec (full schemas): https://porkbun.com/api/json/v3/spec
- Short overview: https://porkbun.com/llms.txt
- Official MCP server: https://github.com/oborseth/Porkbun-MCP (`npx -y @porkbunllc/mcp-server`)
- Create API keys: https://porkbun.com/account/api
