# Porkbun API v3 — API Key Management

> Request and retrieve API keys programmatically

Part of the Porkbun API v3.48. 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/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 (while logged in to Porkbun in that browser) to approve the request. The `authUrl` is valid for **30 minutes** (long enough for a brand-new user to create an account and verify their email); if it lapses, call this endpoint again for a fresh token. After approval, call `/apikey/retrieve` to get the public API key. By default (legacy flow) the secret API key is shown only once, in the user's browser, and must be pasted into the application manually. To let an agent receive the secret without a browser copy, supply a PKCE `codeChallenge` (see the parameter below): the key is then minted lazily and BOTH keys are returned once from `/apikey/retrieve` to the caller presenting the matching `codeVerifier`.

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

SANDBOX: pass `sandbox: true` to skip approval and get a sandbox key pair back immediately (both `apikey` and `secretapikey` in the response, plus `sandbox: true`).

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | no | Human-readable name for the application/integration requesting access. Optional but strongly recommended: it is shown to the account holder on the approval screen so they know what they're granting access to. If omitted, the approval page shows "No application name was provided." |
| `codeChallenge` | string | no | Optional PKCE (RFC 7636) binding. base64url(SHA-256(codeVerifier)), 43 unpadded chars. When present, the request uses lazy-mint delivery: after approval the account holder copies nothing, and /apikey/retrieve returns BOTH the public and secret keys once to the caller that presents the matching codeVerifier. Omit for the legacy flow (secret shown only in the browser). |
| `codeChallengeMethod` | string | no | PKCE challenge method. Only S256 is supported. |
| `sandbox` | boolean | no | If true, skip the approval flow and immediately return a throwaway SANDBOX key pair (public `pk1_sb_`, secret `sk1_sb_`) for a fresh test account seeded with $1000 fake credit. Use it as apikey/secretapikey against the same base URL to run the whole API in the isolated sandbox — no real registry actions, DNS changes, or charges. Rate-limited (20/IP/hour). |
| `returnUrl` | string | no | Optional app-return redirect for native/mobile apps. Requires codeChallenge (PKCE). Must be an HTTPS URL your app claims as a Universal Link / App Link (custom URI schemes are rejected — another app could hijack them). After the account holder approves in the system browser, Porkbun redirects here with `?status=approved&requestToken=<token>` (or `status=denied`) so control returns to your app; it then calls /apikey/retrieve with its codeVerifier to receive the keys. The key is NEVER placed in the redirect — only the request token and status. |

```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 (30 minutes from creation) |
| `deliveryMode` | string | Which delivery flow this request uses. 'pkce' when a codeChallenge was supplied (the secret is returned once via /apikey/retrieve to the verifier holder); 'legacy' otherwise (the secret is shown only in the browser). |
| `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. For a **PKCE** request (one created with a codeChallenge), pass the matching codeVerifier here to receive BOTH keys once (secretapikey is included in that single response and never again); the secret must be claimed within 10 minutes of approval or the request expires (`REQUEST_EXPIRED`) and a new one is needed. For a **legacy** request 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. |
| `codeVerifier` | string | no | PKCE verifier (RFC 7636). Required only when the request was created with a codeChallenge. The high-entropy secret whose base64url(SHA-256(codeVerifier)) equals that challenge. When it matches, this response includes the secret key (secretapikey) once. |

```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. |
| `secretapikey` | string | The secret API key. Returned ONLY for a PKCE request, exactly once, on the retrieve that presents a valid codeVerifier. Never returned for legacy requests, and never again after the first successful claim (later calls return apikey only, with code SECRET_ALREADY_CLAIMED). Store it immediately. |
| `message` | string |  |
| `code` | string | Machine-readable error code. Present when status is ERROR. |

---

## 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://porkbun.com/mcp (`npx -y @porkbunllc/mcp-server`)
- Create API keys: https://porkbun.com/account/api
