# Porkbun API v3 — Account

> Account credit balance, API spend controls, and onboarding

Part of the Porkbun API v3.63. 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

## GET /api/json/v3/account/autoTopup

**Read auto top-up settings**

What auto top-up is set to, whether a payment method is on file, and what `POST /account/topup` would charge right now (`effectiveAmount`).

Auto top-up is how unattended work stops dead-ending on `INSUFFICIENT_FUNDS`: when an order drops the balance below `threshold`, Porkbun charges the saved payment method for `amount` and adds the credit.

If `paymentMethodOnFile` is false the settings are inert and the response says so in `warnings` — a card can only be saved on the website, never over the API.

**Key permission needed:** `funding`

Response fields (AutoTopupResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `enabled` | boolean |  |
| `threshold` | integer | Balance in cents below which a top-up fires; null when auto top-up is off. |
| `amount` | integer | Configured amount to add, in cents; null when never set. |
| `effectiveAmount` | integer | What POST /account/topup would charge right now: the configured amount, or the $50 default. |
| `defaultAmount` | integer | The platform default used when the account has configured nothing. |
| `paymentMethodOnFile` | boolean | False means nothing can be charged and auto top-up cannot fire, whatever the settings say. |
| `balance` | integer | Current account credit, in cents. |
| `chargesToday` | integer |  |
| `chargesThisMonth` | integer |  |
| `maxChargesPerDay` | integer |  |
| `maxChargesPerMonth` | integer |  |
| `warnings` | string[] | Advisory. Present when the settings cannot do anything as they stand, e.g. no payment method is saved. |

## POST /api/json/v3/account/autoTopup

**Configure auto top-up**

Set the top-up amount, and/or switch auto top-up on or off.

`amount` stands on its own. It is what a top-up adds, and `POST /account/topup` charges the same figure on demand, so setting it without automating anything is a normal call: `{"amount": 10000}`.

`enabled: true` additionally makes it fire by itself and requires `threshold` (the balance, in integer US cents, below which a top-up happens) plus an amount — either in the same call or already on file. `enabled: false` stops it firing on a threshold and **keeps the amount**, because on-demand top-ups still use it.

**`amount` set over the API is capped at $500** ($5 minimum), because that value is also what `POST /account/topup` charges — an API key free to set it to anything would be setting its own limit, which is not a limit. A larger amount set by the account holder at porkbun.com/account/api is honoured as-is.

No card is touched here, and one cannot be added over the API. Supports `dryRun`.

**Key permission needed:** `funding`

| Parameter | In | Required | Description |
|---|---|---|---|
| `Idempotency-Key` | header | no | Optional, recommended on every purchase and write. Send a unique value (a UUID) per intended operation. Repeating the request with the same key and an identical body within 24 hours returns the original response instead of doing it again, so a retry after a timeout cannot double-charge or double-apply. The same key with a different body returns 409 IDEMPOTENCY_KEY_MISMATCH; while the first request is still running, 409 IDEMPOTENCY_KEY_IN_USE. |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `enabled` | boolean | yes | Turn auto top-up on or off. Omit to change only the amount. |
| `threshold` | integer | no | Balance in integer US cents below which a top-up fires. Required when enabling, and meaningless without it. |
| `amount` | integer | no | What a top-up adds, in integer US cents. Can be sent on its own; required when enabling if none is on file. Over the API: 500-50000. |
| `dryRun` | boolean | no | Validate only; change nothing. |

Response fields (AutoTopupResponse):

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `enabled` | boolean |  |
| `threshold` | integer | Balance in cents below which a top-up fires; null when auto top-up is off. |
| `amount` | integer | Configured amount to add, in cents; null when never set. |
| `effectiveAmount` | integer | What POST /account/topup would charge right now: the configured amount, or the $50 default. |
| `defaultAmount` | integer | The platform default used when the account has configured nothing. |
| `paymentMethodOnFile` | boolean | False means nothing can be charged and auto top-up cannot fire, whatever the settings say. |
| `balance` | integer | Current account credit, in cents. |
| `chargesToday` | integer |  |
| `chargesThisMonth` | integer |  |
| `maxChargesPerDay` | integer |  |
| `maxChargesPerMonth` | integer |  |
| `warnings` | string[] | Advisory. Present when the settings cannot do anything as they stand, e.g. no payment method is saved. |

## POST /api/json/v3/account/topup

**Top up account credit now**

**Charges the saved payment method** and adds the money to account credit immediately. The call that unblocks work already in progress: enabling auto top-up does nothing until the next order trips the threshold, which is no help to a caller holding an `INSUFFICIENT_FUNDS` response right now.

**`amount` is optional.** Omitted, it charges the account's configured top-up amount, or $50 if the account has never set one — `amountSource` in the response says which of `configured`, `default` or `request` applied. Supplied, it charges that figure for this call only and leaves the stored setting alone, which is the point: an agent could always have written the amount to `/account/autoTopup` first, and making it do that turns a one-off charge into a silent edit of a setting the customer owns.

The card itself can only be saved outside the API, and a supplied amount is held to the same 500–50000 cent range as one set through `/account/autoTopup`. On top of that the dollars are bounded by the month: the account's **monthly spend limit** caps top-ups as well as domain spend (it is the account saying how much the API may move, and a card charge is the API moving money), and an account that has never set one still gets a $100/month ceiling. Card, auto top-ups set off by an API purchase, MPP and USDC all count toward it — `monthlyCeiling` and `ceilingSource` on `GET /account/autoTopup` say which is in force. Top-ups are also limited to 5 per day and 20 per month (`TOPUP_LIMIT_EXCEEDED`), and every successful charge emails the account holder.

Errors that matter: `NO_PAYMENT_METHOD` (nothing saved to charge — the account holder has to save a card or buy credit on the website), `CARD_DECLINED` (nothing was added; the card needs attention), `TOPUP_FAILED` (nothing was charged; retry once).

With a sandbox key this grants simulated credit and charges nothing (`simulated: true`). Supports `dryRun`, which previews the amount and charges nothing in any environment. Honours `Idempotency-Key`.

**Key permission needed:** `funding`

| Parameter | In | Required | Description |
|---|---|---|---|
| `Idempotency-Key` | header | no | Optional, recommended on every purchase and write. Send a unique value (a UUID) per intended operation. Repeating the request with the same key and an identical body within 24 hours returns the original response instead of doing it again, so a retry after a timeout cannot double-charge or double-apply. The same key with a different body returns 409 IDEMPOTENCY_KEY_MISMATCH; while the first request is still running, 409 IDEMPOTENCY_KEY_IN_USE. |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `amount` | integer | no | Optional one-off amount in integer US cents (500-50000). Omit to charge the amount the account has configured, which is the common case. A supplied amount does NOT change the stored setting — use it instead of rewriting the customer's configuration for a single charge. |
| `dryRun` | boolean | no | Preview the charge without making it. |

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `charged` | integer | Amount charged, in cents. |
| `chargedDisplay` | string |  |
| `balance` | integer | Account credit balance after the top-up, in cents. |
| `balanceDisplay` | string |  |
| `orderId` | integer | The captured order recorded for the purchase, 0 if order creation failed (the credit is granted either way). |
| `usedDefaultAmount` | boolean | True when the account had no configured amount and the $50 default was charged. |
| `chargesToday` | integer |  |
| `chargesThisMonth` | integer |  |
| `simulated` | boolean | Sandbox only: credit was granted without charging a card. |
| `message` | 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.

**Key permission needed:** `account`

| Parameter | In | Required | Description |
|---|---|---|---|
| `Idempotency-Key` | header | no | Optional, recommended on every purchase and write. Send a unique value (a UUID) per intended operation. Repeating the request with the same key and an identical body within 24 hours returns the original response instead of doing it again, so a retry after a timeout cannot double-charge or double-apply. The same key with a different body returns 409 IDEMPOTENCY_KEY_MISMATCH; while the first request is still running, 409 IDEMPOTENCY_KEY_IN_USE. |

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.

**Key permission needed:** `read`

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

**Key permission needed:** `read`

| 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/invoices

**List invoices**

The account's invoices (one per order), newest first, like the orders page on porkbun.com. `total_cents` is what the customer was left paying: refunds are netted out, and `state` says whether the invoice is paid, refunded or partially refunded. Any of the account's API keys can read them.

**Key permission needed:** `read`

| 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) |
| `year` | query | no | Only invoices from this year, e.g. 2026. |
| `start` | query | no | Offset for paging. |
| `limit` | query | no |  |

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

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `invoices` | object[] |  |
| `total` | integer | How many invoices match (for paging). |
| `start` | integer |  |
| `limit` | integer |  |
| `years` | integer[] | Years that have invoices. |

## GET /api/json/v3/account/invoice/{orderId}

**Get an invoice**

One invoice as data: bill-to details, payment method (card brand and last four only), each line with its term and resulting domain expiry, and gross, refunded and net totals. The same figures as the PDF.

**Key permission needed:** `read`

| 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) |
| `orderId` | path | yes | Invoice (order) ID, from `GET /account/invoices`. |

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

Response fields:

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

## GET /api/json/v3/account/invoicePdf/{orderId}

**Download an invoice PDF**

The invoice as a PDF, base64-encoded in `contentBase64`: the same document as Download PDF on porkbun.com. Decode it and save it as `filename`.

**Key permission needed:** `read`

| 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) |
| `orderId` | path | yes | Invoice (order) ID, from `GET /account/invoices`. |

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

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `filename` | string |  |
| `contentType` | string |  |
| `sizeBytes` | integer |  |
| `contentBase64` | string |  |
| `downloadUrl` | string | Downloads the PDF without signing in, for 15 minutes (null on a sandbox key). The link to hand a user. |
| `downloadExpires` | string | When downloadUrl stops working (ISO 8601). |

## POST /api/json/v3/account/topupCrypto

**Top up credit with USDC (x402)**

Opens a Coinbase checkout to add account credit with USDC on Base, the same as Use Crypto on porkbun.com. Nothing is charged by this call. Pay it either way:

- `x402Url`: an AI agent with a wallet pays it directly over x402 (a gasless USDC transfer on Base, no page or person involved). This is how an agent gets past `INSUFFICIENT_FUNDS` on an account with no saved card.
- `payUrl`: a Coinbase page where a person pays.

Credit is added when the payment confirms (usually within a minute), less Coinbase's fee of about 1% (`estimatedFee_cents`, `estimatedCredit_cents`). To cover an `INSUFFICIENT_FUNDS` shortfall exactly, open the checkout for its `usdcAmountToCover`. Check `GET /account/topupCryptoStatus/{checkoutId}`. Payments are on-chain and cannot be reversed. Needs a verified email and phone and an account that is not bank-transfer-only (`CRYPTO_NOT_AVAILABLE` otherwise). Up to 10 checkouts per account per day. USDC counts toward the same monthly ceiling as card and MPP top-ups (the monthly spend limit, or $100 when none is set), checked when the checkout is opened, with unpaid checkouts from the last day counted as if paid (`TOPUP_LIMIT_EXCEEDED`). Not available on sandbox keys (`SANDBOX_UNSUPPORTED`; use `/sandbox/topup`).

**Key permission needed:** `funding`

| 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) |
| `Idempotency-Key` | header | no | Optional, recommended on every purchase and write. Send a unique value (a UUID) per intended operation. Repeating the request with the same key and an identical body within 24 hours returns the original response instead of doing it again, so a retry after a timeout cannot double-charge or double-apply. The same key with a different body returns 409 IDEMPOTENCY_KEY_MISMATCH; while the first request is still running, 409 IDEMPOTENCY_KEY_IN_USE. |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `amount` | integer | yes | Checkout amount in integer US cents, $1.00 to $500.00 (100 = $1.00). Credit is this less Coinbase's fee of about 1%. |
| `dryRun` | boolean | no | Check eligibility only; creates no checkout. |

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `checkoutId` | string |  |
| `amount_cents` | integer |  |
| `estimatedFee_cents` | integer | Coinbase's fee, about 1%, estimated high. |
| `estimatedCredit_cents` | integer | Credit the account gets once it is paid. |
| `currency` | string |  |
| `network` | string |  |
| `payUrl` | string | Coinbase-hosted page for a person to pay. |
| `x402Url` | string | x402 payment URL an agent's wallet pays directly. |
| `expiresAt` | string |  |
| `message` | string |  |

## GET /api/json/v3/account/topupCryptoStatus/{checkoutId}

**Status of a USDC top-up**

Whether a checkout from `POST /account/topupCrypto` has been paid and credited. `state`: ACTIVE (not paid), PROCESSING (paid, being credited), COMPLETED (credited), EXPIRED or FAILED. Returns the current balance too.

**Key permission needed:** `read`

| 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) |
| `checkoutId` | path | yes |  |

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `checkoutId` | string |  |
| `state` | string |  |
| `credited` | boolean |  |
| `balance_cents` | integer |  |
| `balance` | string |  |

## POST /api/json/v3/account/verifyPhone/send

**Text a verification code to the account's phone**

Purchases and top-ups need a verified email address and phone number, and accounts are not asked to verify the phone at signup, so `VERIFICATION_REQUIRED` is often the first error a new account sees. This texts (or calls) a code to the phone number already on the account; ask the person for it and pass it to `POST /account/verifyPhone/confirm`. The number cannot be chosen or changed over the API.

One live code at a time: a second request within 10 minutes returns `codeAlreadySent` instead of texting again (send `"channel": "call"` if the text never arrived). Up to 5 sends a day (`PHONE_VERIFY_LIMIT`). Numbers in countries where the website requires a CAPTCHA for phone verification, or accounts with no phone number, get `PHONE_VERIFY_UNAVAILABLE` and verify on the website. Already verified: `alreadyVerified: true`. Not available on sandbox keys.

**Key permission needed:** `account`

| 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) |
| `Idempotency-Key` | header | no | Optional, recommended on every purchase and write. Send a unique value (a UUID) per intended operation. Repeating the request with the same key and an identical body within 24 hours returns the original response instead of doing it again, so a retry after a timeout cannot double-charge or double-apply. The same key with a different body returns 409 IDEMPOTENCY_KEY_MISMATCH; while the first request is still running, 409 IDEMPOTENCY_KEY_IN_USE. |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `channel` | string | no | `call` reads the code out by voice, for when a text does not arrive. |
| `dryRun` | boolean | no |  |

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `sent` | boolean |  |
| `codeAlreadySent` | boolean |  |
| `alreadyVerified` | boolean |  |
| `channel` | string |  |
| `sentTo` | string | The number, masked (`+1 ••• 42`). |
| `expiresInMinutes` | integer |  |
| `retryAfterSeconds` | integer |  |
| `message` | string |  |

## POST /api/json/v3/account/verifyPhone/confirm

**Confirm the phone verification code**

Marks the account's phone number verified with the code from `POST /account/verifyPhone/send`. A wrong or expired code returns `PHONE_CODE_INVALID`; 10 wrong codes in an hour lock it for the hour (`PHONE_VERIFY_LIMIT`). The response says whether the email address is verified too, since purchases need both; verify the email the same way with `POST /account/verifyEmail/send` and `/confirm`.

**Key permission needed:** `account`

| 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) |
| `Idempotency-Key` | header | no | Optional, recommended on every purchase and write. Send a unique value (a UUID) per intended operation. Repeating the request with the same key and an identical body within 24 hours returns the original response instead of doing it again, so a retry after a timeout cannot double-charge or double-apply. The same key with a different body returns 409 IDEMPOTENCY_KEY_MISMATCH; while the first request is still running, 409 IDEMPOTENCY_KEY_IN_USE. |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | string | yes | The code the person received, exactly as texted. Case does not matter. |

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `verified` | boolean |  |
| `alreadyVerified` | boolean |  |
| `emailVerified` | boolean |  |
| `message` | string |  |

## POST /api/json/v3/account/verifyEmail/send

**Email a verification code to the account's address**

Purchases and top-ups need a verified email address and phone number (`VERIFICATION_REQUIRED`). This emails the usual verification message, a link plus a code, to the address on the account (or to a new address waiting for verification). Whoever reads that inbox, the person or an agent with access to it, passes the code to `POST /account/verifyEmail/confirm`; clicking the link works too. The address cannot be chosen or changed over the API, and the code is never returned by the API or in a webhook: it only proves anything because it arrives in that inbox.

A second request within 10 minutes returns `codeAlreadySent` instead of emailing again; the earlier code stays valid for 3 days. Accounts subscribed to `account.verification.code_sent` get a webhook when the email goes out. Already verified: `alreadyVerified: true`. Not available on sandbox keys.

**Key permission needed:** `account`

| 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) |
| `Idempotency-Key` | header | no | Optional, recommended on every purchase and write. Send a unique value (a UUID) per intended operation. Repeating the request with the same key and an identical body within 24 hours returns the original response instead of doing it again, so a retry after a timeout cannot double-charge or double-apply. The same key with a different body returns 409 IDEMPOTENCY_KEY_MISMATCH; while the first request is still running, 409 IDEMPOTENCY_KEY_IN_USE. |

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `sent` | boolean |  |
| `codeAlreadySent` | boolean |  |
| `alreadyVerified` | boolean |  |
| `sentTo` | string | The address, masked (`o•••@example.com`). |
| `expiresInMinutes` | integer |  |
| `retryAfterSeconds` | integer |  |
| `message` | string |  |
| `phoneVerified` | boolean |  |

## POST /api/json/v3/account/verifyEmail/confirm

**Confirm the email verification code**

Marks the account's email address verified with the code from the verification email (`POST /account/verifyEmail/send`, or any verification email sent in the last 3 days). The code must belong to this account and to the address being verified. A wrong, expired or superseded code returns `EMAIL_CODE_INVALID`; 10 wrong codes in an hour lock it for the hour (`EMAIL_VERIFY_LIMIT`). The response says whether the phone is verified too, since purchases need both. Accounts subscribed to `account.verification.completed` get a webhook.

**Key permission needed:** `account`

| 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) |
| `Idempotency-Key` | header | no | Optional, recommended on every purchase and write. Send a unique value (a UUID) per intended operation. Repeating the request with the same key and an identical body within 24 hours returns the original response instead of doing it again, so a retry after a timeout cannot double-charge or double-apply. The same key with a different body returns 409 IDEMPOTENCY_KEY_MISMATCH; while the first request is still running, 409 IDEMPOTENCY_KEY_IN_USE. |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `code` | string | yes | The verification code from the email, exactly as written. |

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `verified` | boolean |  |
| `alreadyVerified` | boolean |  |
| `message` | string |  |
| `phoneVerified` | boolean |  |

## POST /api/json/v3/account/topupMpp

**Top up credit with the user's card, paid by an agent (MPP)**

Returns a signed, single-use payment link (valid 15 minutes, bound to this account and amount) that an AI agent pays with the user's **card** over MPP, the Machine Payments Protocol (mpp.dev), using a Stripe Shared Payment Token from its agent wallet (Stripe's Link Agent Wallet). No card needs to be saved at Porkbun. Nothing is charged by this call.

The agent's MPP client requests `payUrl` (POST) and gets HTTP `402` with a `WWW-Authenticate: Payment` challenge (`method="stripe"`, `intent="charge"`); its wallet mints a token for the user's card, the client retries with `Authorization: Payment <credential>`, and the credit is added at once: the response carries `credited_cents` and the new `balance_cents`, with a `Payment-Receipt` header. For example: `npx @stripe/link-cli mpp pay <payUrl> -X POST --context "..."`. Newer Link CLI versions stop after creating the spend request: once the person approves in Link, complete it with `link-cli mpp pay <payUrl> --spend-request-id <lsrq_id> -X POST`. Link refuses a `--context` shorter than 100 characters: it is what the user reads when approving the spend, so say what it is for and the dollar amount, worded fresh for each payment (Link can refuse an identical repeat).

Same limits as card top-ups (`POST /account/topup`): $5-$500, the monthly ceiling, 5 a day and 20 a month (`TOPUP_LIMIT_EXCEEDED`). Not available on sandbox keys (`SANDBOX_UNSUPPORTED`).

**Key permission needed:** `funding`

| 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) |
| `Idempotency-Key` | header | no | Optional, recommended on every purchase and write. Send a unique value (a UUID) per intended operation. Repeating the request with the same key and an identical body within 24 hours returns the original response instead of doing it again, so a retry after a timeout cannot double-charge or double-apply. The same key with a different body returns 409 IDEMPOTENCY_KEY_MISMATCH; while the first request is still running, 409 IDEMPOTENCY_KEY_IN_USE. |

Request body fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `amount` | integer | yes | Credit to add, in integer US cents. |
| `dryRun` | boolean | no | Check eligibility and limits only. |

Response fields:

| Field | Type | Description |
|---|---|---|
| `status` | string |  |
| `amount_cents` | integer |  |
| `payUrl` | string | Signed MPP payment URL; POST it with an MPP client. |
| `expiresAt` | string |  |
| `protocol` | string |  |
| `methods` | string[] |  |
| `message` | string |  |

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

**Get API spend settings**

Returns the account's API spend control settings, this month's spend, and the limits actually enforced: `spendLimit` on purchases and `topupLimit` on credit added over the API. The account holder sets the limit on the website (https://porkbun.com/account/api); it cannot be changed over the API. All amounts are in cents. Authenticate using `X-API-Key` and `X-Secret-API-Key` headers, or `Authorization: Bearer <token>`.

**Key permission needed:** `read`

| 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. |
| `spendLimit` | object | The monthly cap on API purchases actually enforced (registrations, renewals, transfers, closeouts, paid hosting). |
| `topupLimit` | object | The monthly cap on credit added over the API: card top-ups, auto top-ups set off by an API purchase, MPP and USDC together. |

---

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