# Backups

List, create, download and restore backups.

## List backups

`GET /sites/{site_id}/backups` · scope `backups:read`

The site's backups, newest first (automatic daily ones, manual ones and uploaded ones).

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `site_id` | path | integer | yes | The site id (see GET /sites). |
| `state` | query | string (one of: Pending, Creating, Ok, Error, Storing) | no | Only backups in this state. |
| `limit` | query | integer | no | Items per page (1-200). Default: `50`. |
| `cursor` | query | string | no | The next_cursor value of the previous page. Omit for the first page. |

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/sites/123/backups?limit=10" \
  -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/sites/123/backups?limit=10", headers=headers, timeout=120)
print(r.status_code, r.json())
```

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

### Response

`200`

```json
{
  "data": [
    {
      "id": 88001,
      "created_at": "2026-09-19T09:12:00Z",
      "type": "Automatic",
      "state": "Ok",
      "size_bytes": 52428800,
      "is_blueprint": false,
      "locked": false
    }
  ],
  "next_cursor": null,
  "has_more": false
}
```

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

## Create a backup now

`POST /sites/{site_id}/backups` · scope `backups:write`

Takes a full backup (files + database) now, as Create backup in the panel. Follow the job until the backup is Ok.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `site_id` | path | integer | yes | The site id (see GET /sites). |

### Example

```bash
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/backups" \
  -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/sites/123/backups", headers=headers, timeout=120)
print(r.status_code, r.json())
```

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

### Response

`202`

```json
{
  "data": {
    "id": 88001,
    "created_at": "2026-09-19T09:12:00Z",
    "type": "Manual",
    "state": "Pending",
    "size_bytes": 52428800,
    "is_blueprint": false,
    "locked": false
  }
}
```

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

## Get one backup

`GET /sites/{site_id}/backups/{backup_id}` · scope `backups:read`

One backup of the site.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `site_id` | path | integer | yes | The site id (see GET /sites). |
| `backup_id` | path | integer | yes | The backup id. |

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/sites/123/backups/88001" \
  -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/sites/123/backups/88001", headers=headers, timeout=120)
print(r.status_code, r.json())
```

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

### Response

`200`

```json
{
  "data": {
    "id": 88001,
    "created_at": "2026-09-19T09:12:00Z",
    "type": "Automatic",
    "state": "Ok",
    "size_bytes": 52428800,
    "is_blueprint": false,
    "locked": false
  }
}
```

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

## Download link

`POST /sites/{site_id}/backups/{backup_id}/download-link` · scope `backups:read`

A temporary link to download the backup archive (tar.gz) - valid about 4 hours.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `site_id` | path | integer | yes | The site id (see GET /sites). |
| `backup_id` | path | integer | yes | The backup id. |

### Example

```bash
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/backups/88001/download-link" \
  -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/sites/123/backups/88001/download-link", headers=headers, timeout=120)
print(r.status_code, r.json())
```

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

### Response

`200`

```json
{
  "data": {
    "url": "https://dl.dropboxusercontent.com/...",
    "expires_in_seconds": 14400,
    "size_bytes": 52428800
  }
}
```

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

## Restore a backup

`POST /sites/{site_id}/backups/{backup_id}/restore` · scope `backups:write` · **destructive**

Replaces the site's files and database with the backup (as Restore in the panel). Refused while the site is busy, switched off or the plan has expired. Follow the job.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `site_id` | path | integer | yes | The site id (see GET /sites). |
| `backup_id` | path | integer | yes | The backup id. |
| `confirm` | body | boolean | yes | Must be true: this action overwrites the site. |

### Example

```bash
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/backups/88001/restore" \
  -H "Authorization: Bearer $PBN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/backups/88001/restore", headers=headers, json={"confirm": True}, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/backups/88001/restore", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`, "Content-Type": "application/json"},
  body: JSON.stringify({"confirm": true})
});
console.log(res.status, await res.json());
```

### Response

`202`

```json
{
  "data": {
    "restoring": true
  }
}
```

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

## Delete a backup

`DELETE /sites/{site_id}/backups/{backup_id}` · scope `backups:write` · **destructive**

Deletes a backup. A backup that a blueprint uses cannot be deleted.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `site_id` | path | integer | yes | The site id (see GET /sites). |
| `backup_id` | path | integer | yes | The backup id. |

### Example

```bash
curl -s -X DELETE "https://app.pbn.ltd/api/v1/sites/123/backups/88001" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

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

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

### Response

`200`

```json
{
  "data": {
    "deleted": true,
    "id": 88001
  }
}
```

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