# Recover a broken DNS zone

Someone deleted the wrong record, pasted over an MX, or ran a script that
flattened a zone. DNS is the one layer with no undo built in, so the usual
recovery is archaeology: ask the customer what it used to say, guess at the
rest, and hope.

You do not have to do that. Porkbun keeps dated restore points for every zone,
and three calls get you from "it's broken" to "it's back".

## When a restore point exists

You get one automatically:

- **Before the first write to a zone in each hour.** A session of edits costs
  one restore point, not one per record, so the state before that session is
  recoverable.
- **Before any bulk import** (`/dns/import`) or zone wipe.
- **Before any restore**, so a restore can itself be undone.

You do not get one for a change made *outside* Porkbun DNS — if the zone is
served by someone else's nameservers, there is nothing here to restore.

## 1. Find the point you want

```
GET /api/json/v3/dns/history/example.com
{"apikey":"pk1_...","secretapikey":"sk1_..."}
```

```json
{
  "status": "SUCCESS",
  "domain": "example.com",
  "restorePoints": [
    { "id": 1240, "takenAt": "2026-09-16 22:14:19", "reason": "before-delete",
      "source": "auto", "recordCount": 9, "matchesLive": true },
    { "id": 1231, "takenAt": "2026-09-16 09:02:55", "reason": "before-edit",
      "source": "auto", "recordCount": 11, "matchesLive": false }
  ]
}
```

Two things to read carefully:

- `recordCount` is the size of the zone **as it was at that moment**. The jump
  from 11 to 9 is the damage.
- `matchesLive: true` marks the point the zone is sitting on now. That is where
  you are, not where you want to go.

So the interesting point here is **1231**: taken before the edit, 11 records.

## 2. See exactly what you would change

```
GET /api/json/v3/dns/diff/example.com/1231
```

```json
{
  "missing": [
    { "name": "example.com", "type": "MX", "content": "mail.example.com", "ttl": 600, "prio": 10 },
    { "name": "shop.example.com", "type": "A", "content": "203.0.113.10", "ttl": 600, "prio": null }
  ],
  "extra": [
    { "name": "example.com", "type": "MX", "content": "wrong.example.net", "ttl": 600, "prio": 10 }
  ],
  "inSync": false
}
```

- `missing` — in the restore point, not live. A restore **adds these back**.
- `extra` — live, not in the restore point. A restore **leaves these alone**
  unless you ask for `prune`.

Records are matched on name, type, content and priority rather than on id,
because an id means nothing across a delete and re-create.

## 3. Rehearse it

```
POST /api/json/v3/dns/restore/example.com
{"apikey":"pk1_...","secretapikey":"sk1_...","snapshotId":1231,"dryRun":true}
```

The dry run reports exactly what would be added and removed and changes
nothing. Do this first. It costs nothing and it is the difference between
fixing a zone and finding out afterwards.

## 4. Restore

```
POST /api/json/v3/dns/restore/example.com
{"apikey":"pk1_...","secretapikey":"sk1_...","snapshotId":1231}
```

```json
{
  "status": "SUCCESS",
  "restoredFrom": 1231,
  "previousStateSavedAs": 1244,
  "restored": 2,
  "removed": 0
}
```

**`previousStateSavedAs` is your undo.** If the restore was not what you
wanted, restore *that* point and you are back where you started.

In this example the bad `wrong.example.net` MX is still there — a restore only
adds by default. Either delete it directly with `/dns/delete`, or restore again
with `prune`:

```json
{"apikey":"pk1_...","secretapikey":"sk1_...","snapshotId":1231,"prune":true}
```

`prune` is opt-in because "restore my records" usually means "put back what I
lost", not "delete everything I have done since". Only use it when you have
read the `extra` list and want all of it gone.

## Read `failed`

```json
{ "restored": 7, "failed": [ { "name": "example.com", "type": "ALIAS",
  "reason": "refused by the DNS layer" } ] }
```

Parking records and other masked types (`ALIAS`, `HTTPS`) are managed by a
different part of the platform and cannot be recreated through the DNS API. They
are reported in `failed` rather than counted as restored, so `restored` is
always a true number. If a parking record is what is missing, re-park the domain
from the website or ask support.

SOA and NS records are never touched by any of this. The zone's own delegation
is not what anyone means by "my DNS records", and the SOA serial moves on its
own.

## First, make sure this zone is the one being served

A restore puts back the zone **we** hold. If the domain is delegated to another
provider's nameservers, that is not what resolves, and a perfect restore will
change nothing anyone can see. `GET /dns/preflight/{domain}` answers this as the
`nameservers-ours` check, and every DNS write response carries the same fact in
`warnings` when it applies.

## What this is not

- **Not a backup of someone else's DNS.** Restore points only cover the zone as
  Porkbun held it.
- **Not per-second time travel.** You get the points listed in
  `/dns/history`, not an arbitrary timestamp.
- **Not unlimited.** Up to 50 points are returned, newest first.


---

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