# Porkbun API v3 — Domain

> Domain registration, nameservers, URL forwarding, glue records, and auto-renewal

Part of the Porkbun API v3.7. Topic index: https://porkbun.com/llms · Full reference: https://porkbun.com/llms-full.txt · Overview: https://porkbun.com/llms.txt · OpenAPI spec: https://porkbun.com/api/json/v3/spec

**Auth:** send `X-API-Key` / `X-Secret-API-Key` headers (preferred) or `apikey` / `secretapikey` in the JSON body. Create keys at https://porkbun.com/account/api

---

# Endpoints

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

## GET /api/json/v3/domain/getRegistrationRequirements/{tld}

**Get TLD registration requirements (JSON Schema)**

Machine-readable registration requirements for a TLD. Returns whether the TLD is registerable via the API (`apiRegisterable`), the `/domain/create` request body as a JSON Schema, the fixed registration term, WHOIS-privacy/validated-address/registrant-only flags, and — for TLDs with registry eligibility rules (e.g. .us nexus, .ca legal type) — a second JSON Schema (`registryRequirements`) enumerating those fields with allowed values and labels. Call this before /domain/create to know upfront whether and how a TLD can be registered. Read-only; GET (header auth) or POST (body or header auth).

| Parameter | In | Required | Description |
|---|---|---|---|
| `tld` | path | yes | TLD without a leading dot, e.g. `com`, `us`, `ca`. |
| `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/getRegistrationRequirements/us' \
  -H 'X-API-Key: pk1_...' \
  -H 'X-Secret-API-Key: sk1_...'
```

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `tld` | string |  |
| `apiRegisterable` | boolean | Whether this TLD can be registered via the API. False for TLDs with registry eligibility requirements the API cannot submit (register those on the website). |
| `registrationDurationYears` | integer | Fixed registration term the API uses for this TLD. |
| `maxRegistrationYears` | integer | Maximum years the registry allows, or null if unspecified. |
| `whoisPrivacySupported` | boolean |  |
| `requiresValidatedAddress` | boolean |  |
| `registrantOnly` | boolean | TLD uses only the registrant contact (no admin/tech/billing). |
| `requestSchema` | object | JSON Schema (Draft 2020-12) for the /domain/create request body this TLD accepts (cost, agreeToTerms, whoisPrivacy, credentials), including the fixed registration term. |
| `registryRequirements` | object | JSON Schema of extra registry/eligibility fields the TLD requires (e.g. .us purpose+category, .ca legal type) with enums and human labels, plus an x-policyNote. Null when the TLD has no structured extra data. These fields are documented for eligibility; they are not accepted by /domain/create today. |
| `notApiRegisterableReason` | string | Present only when apiRegisterable is false. |

---

## More

- Guides (how-tos): https://porkbun.com/llms/guides
- 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
