# Register a domain end-to-end

A safe registration flow for an autonomous agent: check price, confirm the TLD is registerable, rehearse, then commit idempotently.

## 0. Fund the account first

The step people miss, because nothing about it looks like an API problem: **a purchase spends prepaid account credit, and no card on file is charged at the moment of the call.** A saved card does not make a registration go through — the balance has to cover the price first.

```bash
curl 'https://api.porkbun.com/api/json/v3/account/balance' \
  -H 'X-API-Key: pk1_...' -H 'X-Secret-API-Key: sk1_...'
# -> { "status":"SUCCESS", "balance": 5000, "display": "$50.00" }
```

`balance` is in cents. If the account already has a payment method saved, you can fund it without leaving the API:

```bash
# charge the saved card now and add the credit
curl -X POST https://api.porkbun.com/api/json/v3/account/topup \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_..."}'
# -> { "status":"SUCCESS", "charged":5000, "balance":5000, "amountSource":"default", ... }
```

With no `amount` it charges the account's configured top-up amount, or $50 if none was ever set (`amountSource` says which). Send `amount` (integer cents, 500-50000) for a one-off figure, such as exactly what an order needs; it does not change the saved setting. Top-ups are capped per day and per month and count against the monthly API spend limit, every charge is emailed to the account holder, and a card can only be saved on the website. **Charging a card is the user's call: ask them, with the amount, before calling this.** Set a standing amount, and make it automatic, with:

```bash
curl -X POST https://api.porkbun.com/api/json/v3/account/autoTopup \
  -H 'Content-Type: application/json' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","enabled":true,"threshold":2000,"amount":10000}'
```

That tops up $100 whenever an order leaves the balance under $20, so an unattended agent does not stop mid-task. Send `amount` on its own if you only want to set what a top-up adds without automating it — `/account/topup` charges that same figure, and turning auto top-up off later keeps it. If `paymentMethodOnFile` comes back false, nothing can be charged: add credit or save a card at [Account Credit](https://porkbun.com/account/credit).

The account's email and phone must also be verified (`VERIFICATION_REQUIRED` otherwise; an agent can verify both, see https://porkbun.com/llms/guides/verify-the-account), and a single API registration is capped at $100 — above that, register on the website.

If you try anyway, the refusal tells you the gap, and whether a top-up can close it:

```json
{
  "status": "ERROR",
  "code": "INSUFFICIENT_FUNDS",
  "cost": 1106, "balance": 0, "shortfall": 1106,
  "topUpAvailable": true, "topUpAmountToCover": 1106,
  "next_action": { "type": "top_up", "retryable": false, "hint": "A payment method is saved on this account, so ..." },
  "message": "Registration of example.com costs $11.06 but your Porkbun account credit is $0.00, so you are $11.06 short. ..."
}
```

`topUpAvailable: true` means a card is saved: offer the user a top-up of at least `topUpAmountToCover`, and with their OK call `POST /account/topup`, then retry the same request unchanged. `false` means there is no saved card to charge: the user can pay with their card from a Stripe Link agent wallet (`POST /account/topupMpp`), with USDC (`POST /account/topupCrypto` for `usdcAmountToCover`), or add credit on the website. A dry run that would come up short reports the same fields, so you can sort out funding before the real call.

**Or skip the credit: pay for the registration directly by card** (`"payWith": "card"`: pay the returned link from a Stripe Link agent wallet, then repeat with `cardPaymentId`; the card is held and charged only if the registration succeeds; see [Pay with the user's card (MPP)](https://porkbun.com/llms/guides/pay-with-card-mpp)), **or in USDC.** Add `"payWith": "usdc"` to the create call. It answers HTTP 402 with x402 terms in the `PAYMENT-REQUIRED` header; an x402 client pays them and repeats the same request with `PAYMENT-SIGNATURE`, and the domain is registered in that call. If the registration fails after payment, the payment stays on the account as credit (it is not sent back to the wallet), so you can retry from credit. See [Pay with USDC (x402)](https://porkbun.com/llms/guides/pay-with-usdc-x402).

## 1. Check availability and price

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

The response includes availability and the registration price. **Prices are in USD; `/domain/create` wants the cost in integer US cents and rejects a mismatch** — so carry the quote forward.

**Taken?** A name registered to someone else may still be for sale. The response then carries `aftermarketSearch`: `GET /aftermarket/search?query=example.com` lists any aftermarket listing with its price, what that price means (`saleType`: buy now, minimum offer or current bid) and a `buyUrl`. Aftermarket names are bought on porkbun.com, not over the API.

## 2. Confirm the TLD is registerable via the API

Some TLDs have registry eligibility rules the API can't submit (e.g. `.us`, `.ca`, `.eu`, `.au`) and are website-only. Check first instead of discovering this from a failed call:

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

Look at `apiRegisterable`. If `false`, register on the website. If `true`, `requestSchema` is the JSON Schema for the create body, and `registrationDurationYears` is the fixed term used.

## 3. Rehearse with a dry run

Pass `dryRun: true` to run every pre-flight check (availability, price match, eligibility, funds, spend limit) **without charging or creating anything** — it doesn't even consume your create rate-limit budget:

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

The preview returns `wouldSucceed`, `cost`, `costDisplay`, `balance`, `sufficientFunds`, and (when a limit applies) `monthlySpendLimit`, `limitSource` and `withinMonthlySpendLimit`. Only commit if `wouldSucceed` is true.

Two things worth knowing: send `"cost": 0` with `dryRun` if you do not know the price yet and want to be quoted, and a dry run is answered even when the balance is too low — `wouldSucceed` comes back false with `shortfall` telling you how much to add. Rehearsing is exactly how you find out what an order needs.

## 4. Register (for real, and safely)

Send an **`Idempotency-Key`** header — a retry within 24h replays the original result instead of registering (or charging) twice.

```bash
curl -X POST https://api.porkbun.com/api/json/v3/domain/create/example.com \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7' \
  -d '{"apikey":"pk1_...","secretapikey":"sk1_...","cost":1000,"agreeToTerms":"yes"}'
```

- `cost` — integer US cents, must match the current quote from step 1.
- `agreeToTerms` — must be `"yes"`; you're accepting the registration agreement, ToS, privacy policy, and auto-renew terms.
- Your account email and phone must be verified before the first registration.

## 5. Verify

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

## Guardrails worth setting

- **Spend controls** — a monthly API limit, low-balance alerts, and auto top-up (on the [API settings page](https://porkbun.com/account/api)). The limit covers every API purchase and, separately, all credit added over the API; it is $100 a month until the account holder sets their own. A registration that would exceed it is blocked with `MONTHLY_SPEND_LIMIT_EXCEEDED`.
- **Per-key scoping** — restrict the key to the domains/IPs it needs. Registering needs the key's `purchase` permission; a key set to **Manage, but no purchases** can still rehearse it with `dryRun`.

## Related

- [Getting started](https://porkbun.com/llms/guides/getting-started) · [Dynamic DNS](https://porkbun.com/llms/guides/dynamic-dns)
- Domain endpoints: https://porkbun.com/llms/domain


---

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