# Wayback restore

Rebuild a website from the public web archive: look a domain up, pick a day, see what would be rebuilt and order it with a restore credit.

## Wayback restore: credits, prices and restores

`GET /wayback` · scope `wayback:read`

Your restore credits, the price of a restore of each kind (what the page would charge, with your VAT), how many new sites your plan still has room for, and your restores, newest first.

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/wayback" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/wayback", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/wayback", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "available": true,
    "credits": 3,
    "free_site_slots": 12,
    "site_limit": 50,
    "prices": {
      "static": {
        "price": "9.00",
        "vat": "0",
        "charge": "9.00"
      },
      "wordpress": {
        "price": "19.00",
        "vat": "0",
        "charge": "19.00"
      }
    },
    "restores": [
      {
        "id": 812,
        "reference": "WB-7Q2K9",
        "domain": "old-site.com",
        "day": "2019-05-14",
        "output": "static",
        "target_domain": "old-site.com",
        "target": "new",
        "site_id": null,
        "state": "planned",
        "state_label": "Ready to order",
        "pay_status": "unpaid",
        "pay_label": "Not paid",
        "message": "",
        "pages": 38,
        "assets": 212,
        "archive_mb": 14.2,
        "fetched": 0,
        "missing": 0,
        "failed": 0,
        "progress_pct": 0,
        "running": false,
        "done": false,
        "created_at": "2026-09-26T10:00:00Z"
      }
    ],
    "page_url": "https://app.pbn.ltd/wayback/"
  }
}
```

Errors: `rate_limited`, `scope_missing`, `unauthorized`

## Days the archive holds

`GET /wayback/archive/{domain}` · scope `wayback:read`

The days the public web archive has a copy of the domain's home page on, as the calendar shows. The first look-up of a domain reads the archive in the background: `state` is "pending" - ask again in a minute until it reads "ready" (or "error", with the reason).

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain` | path | string | yes | The domain, e.g. old-site.com. |

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/wayback/archive/domain" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/wayback/archive/domain", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/wayback/archive/domain", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "domain": "old-site.com",
    "state": "ready",
    "days": [
      "2019-05-14",
      "2019-06-02"
    ],
    "truncated": false,
    "error": ""
  }
}
```

Errors: `not_found`, `rate_limited`, `scope_missing`, `unauthorized`, `unavailable`, `validation_failed`

## One restore

`GET /wayback/restores/{restore_id}` · scope `wayback:read`

One restore: its progress and, while it is "planned" (ready to order), a sample of the pages that would be rebuilt and your existing sites it could be restored into.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `restore_id` | path | integer | yes | The restore id. |

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/wayback/restores/restore_id" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/wayback/restores/restore_id", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/wayback/restores/restore_id", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "id": 812,
    "reference": "WB-7Q2K9",
    "domain": "old-site.com",
    "day": "2019-05-14",
    "output": "static",
    "target_domain": "old-site.com",
    "target": "new",
    "site_id": null,
    "state": "planned",
    "state_label": "Ready to order",
    "pay_status": "unpaid",
    "pay_label": "Not paid",
    "message": "",
    "pages": 38,
    "assets": 212,
    "archive_mb": 14.2,
    "fetched": 0,
    "missing": 0,
    "failed": 0,
    "progress_pct": 0,
    "running": false,
    "done": false,
    "created_at": "2026-09-26T10:00:00Z",
    "sample": [
      "/",
      "/about/",
      "/contact/"
    ],
    "existing_sites": {
      "static": [
        {
          "id": 123,
          "domain": "example.com"
        }
      ],
      "wordpress": []
    }
  }
}
```

Errors: `not_found`, `rate_limited`, `scope_missing`, `unauthorized`

## Start a restore (free)

`POST /wayback/restores` · scope `wayback:write`

Reads that day's copy from the archive and works out what would be rebuilt. Nothing is charged: the restore waits at "planned" until you order it.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain` | body | string | yes | The domain to rebuild. |
| `day` | body | string | yes | A day from GET /wayback/archive/{domain} (YYYY-MM-DD). |
| `output` | body | string (one of: static, wordpress) | no | Static HTML, or converted to WordPress (changeable when ordering). Default: `static`. |

### Example

```bash
curl -s -X POST "https://app.pbn.ltd/api/v1/wayback/restores" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/wayback/restores", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/wayback/restores", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`201`

```json
{
  "data": {
    "id": 812,
    "reference": "WB-7Q2K9",
    "domain": "old-site.com",
    "day": "2019-05-14",
    "output": "static",
    "target_domain": "old-site.com",
    "target": "new",
    "site_id": null,
    "state": "planned",
    "state_label": "Ready to order",
    "pay_status": "unpaid",
    "pay_label": "Not paid",
    "message": "",
    "pages": 38,
    "assets": 212,
    "archive_mb": 14.2,
    "fetched": 0,
    "missing": 0,
    "failed": 0,
    "progress_pct": 0,
    "running": false,
    "done": false,
    "created_at": "2026-09-26T10:00:00Z"
  }
}
```

Errors: `rate_limited`, `scope_missing`, `unauthorized`, `unavailable`, `validation_failed`

## Order a restore with a restore credit

`POST /wayback/restores/{restore_id}/order` · scope `wayback:write`

Uses ONE restore credit and starts the rebuild (restore credits are bought in packs on the Wayback page; paying one restore by PayPal or crypto is done there too). Restoring into an existing site replaces what it serves.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `restore_id` | path | integer | yes | The restore id. |
| `output` | body | string (one of: static, wordpress) | yes | Static HTML or converted to WordPress. |
| `target` | body | string (one of: new, existing) | yes | "new" makes a new site (needs a free site slot); "existing" restores into one of your sites of that type (it is overwritten). |
| `new_domain` | body | string | no | For a new site: its domain (default: the old domain). |
| `site_id` | body | integer | no | For an existing site: its id. |
| `accept_terms` | body | boolean | yes | Must be true: you agree to the Terms and Conditions (https://pbn.ltd/terms/), recorded exactly like the box on the order page. |
| `ack_rights` | body | boolean | yes | Must be true: you confirm you have the right to republish this content (the trademark / copyright acknowledgement on the order page). |

### Example

```bash
curl -s -X POST "https://app.pbn.ltd/api/v1/wayback/restores/restore_id/order" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/wayback/restores/restore_id/order", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/wayback/restores/restore_id/order", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`202`

```json
{
  "data": {
    "id": 812,
    "reference": "WB-7Q2K9",
    "domain": "old-site.com",
    "day": "2019-05-14",
    "output": "static",
    "target_domain": "old-site.com",
    "target": "new",
    "site_id": null,
    "state": "queued",
    "state_label": "Ready to order",
    "pay_status": "paid",
    "pay_label": "Paid",
    "message": "",
    "pages": 38,
    "assets": 212,
    "archive_mb": 14.2,
    "fetched": 0,
    "missing": 0,
    "failed": 0,
    "progress_pct": 0,
    "running": false,
    "done": false,
    "created_at": "2026-09-26T10:00:00Z",
    "credits_left": 2
  }
}
```

Errors: `conflict`, `not_found`, `payment_required`, `rate_limited`, `scope_missing`, `unauthorized`, `unavailable`, `validation_failed`
