Extra databases
More databases for one site (any site type with a database), each with its own daily backup you can restore.
List extra databases
GET/api/v1/extra-databasesScope extradb:read
Every extra database in your account (all sites), with its state, size, paid period and last backup. Credentials are shown on the site's Extra databases page, never here.
Example
curl -s "https://app.pbn.ltd/api/v1/extra-databases" \
-H "Authorization: Bearer $PBN_API_KEY"
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/extra-databases", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/extra-databases", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": [
{
"id": 311,
"site_id": 12345,
"site_domain": "example.com",
"name": "site12345_shop",
"label": "shop",
"state": "ready",
"state_label": "Ready",
"read_only": false,
"size_bytes": 5242880,
"measured_at": "2026-09-24T06:00:00Z",
"paid_until": "2026-11-01",
"last_paid_day": "2026-10-31",
"in_grace": false,
"cancel_at_end": false,
"last_backup_at": "2026-09-24T03:10:00Z",
"restore_pending": false,
"error": "",
"created_at": "2026-09-24T09:00:00Z",
"manage_url": "https://app.pbn.ltd/extra-databases/site/12345/"
}
]
}
Errors: rate_limited, scope_missing, unauthorized
Extra databases of a site, price and allowance
GET/api/v1/sites/{site_id}/extra-databasesScope extradb:read
Whether this site can have extra databases (every site type with a database), the price per database per month (before VAT, from the live price row), how many a site may have, the site's database allowance shared by its main and extra databases, and its extra databases. Buying or renewing is done on checkout_url in the browser: the API never starts a payment.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
Example
curl -s "https://app.pbn.ltd/api/v1/sites/123/extra-databases" \
-H "Authorization: Bearer $PBN_API_KEY"
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/sites/123/extra-databases", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/extra-databases", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"available": true,
"site_has_database": true,
"sales_open": true,
"included_with_staff_account": false,
"price_usd_per_month": "1.00",
"vat_percent": 0.0,
"months_offered": [
1,
3,
6,
12
],
"max_per_site": 10,
"backups_kept_days": 7,
"removed_copy_kept_days": 30,
"grace_days": 7,
"usage": {
"main_bytes": 20971520,
"extra_bytes": 5242880,
"used_bytes": 26214400,
"limit_mib": 2048,
"over": false
},
"databases": [
{
"id": 311,
"site_id": 12345,
"site_domain": "example.com",
"name": "site12345_shop",
"label": "shop",
"state": "ready",
"state_label": "Ready",
"read_only": false,
"size_bytes": 5242880,
"measured_at": "2026-09-24T06:00:00Z",
"paid_until": "2026-11-01",
"last_paid_day": "2026-10-31",
"in_grace": false,
"cancel_at_end": false,
"last_backup_at": "2026-09-24T03:10:00Z",
"restore_pending": false,
"error": "",
"created_at": "2026-09-24T09:00:00Z",
"manage_url": "https://app.pbn.ltd/extra-databases/site/12345/"
}
],
"checkout_url": "https://app.pbn.ltd/extra-databases/site/12345/"
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized
One extra database
GET/api/v1/sites/{site_id}/extra-databases/{db_id}Scope extradb:read
One extra database, with its restore points (its own daily backups, newest first; the id is what extradb.restore takes).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
db_id | path | integer | yes | The extra database id. |
Example
curl -s "https://app.pbn.ltd/api/v1/sites/123/extra-databases/db_id" \
-H "Authorization: Bearer $PBN_API_KEY"
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/sites/123/extra-databases/db_id", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/extra-databases/db_id", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"id": 311,
"site_id": 12345,
"site_domain": "example.com",
"name": "site12345_shop",
"label": "shop",
"state": "ready",
"state_label": "Ready",
"read_only": false,
"size_bytes": 5242880,
"measured_at": "2026-09-24T06:00:00Z",
"paid_until": "2026-11-01",
"last_paid_day": "2026-10-31",
"in_grace": false,
"cancel_at_end": false,
"last_backup_at": "2026-09-24T03:10:00Z",
"restore_pending": false,
"error": "",
"created_at": "2026-09-24T09:00:00Z",
"manage_url": "https://app.pbn.ltd/extra-databases/site/12345/",
"restore_points": [
{
"id": "site12345_shop-20260924-0310.sql.gz",
"taken_at": 1790219400,
"bytes": 1048576
}
]
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized
Add an extra database
POST/api/v1/sites/{site_id}/extra-databasesScope extradb:write
Adds an extra database where your account gets them free of charge (created in a minute or two). Every other account buys one in the browser: this answers 402 payment_required with details.checkout_url and the price, and never starts a payment.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
label | body | string | yes | The name suffix: 1 to 16 lowercase letters and digits. The database is called site<site id>_<label>. |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/extra-databases" \
-H "Authorization: Bearer $PBN_API_KEY"
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/extra-databases", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/extra-databases", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
202
{
"data": {
"id": 311,
"site_id": 12345,
"site_domain": "example.com",
"name": "site12345_shop",
"label": "shop",
"state": "ready",
"state_label": "Ready",
"read_only": false,
"size_bytes": 5242880,
"measured_at": "2026-09-24T06:00:00Z",
"paid_until": "2026-11-01",
"last_paid_day": "2026-10-31",
"in_grace": false,
"cancel_at_end": false,
"last_backup_at": "2026-09-24T03:10:00Z",
"restore_pending": false,
"error": "",
"created_at": "2026-09-24T09:00:00Z",
"manage_url": "https://app.pbn.ltd/extra-databases/site/12345/"
}
}
Errors: not_found, payment_required, rate_limited, scope_missing, unauthorized, unavailable, validation_failed
Cancel (or keep) an extra database at the end of its paid months
POST/api/v1/sites/{site_id}/extra-databases/{db_id}/cancelScope extradb:write · destructive
Cancelling stops the renewal reminders and removes the database (full copy first) when its paid months end; nothing more is charged. Send "cancel": false to keep it. Needs "confirm": true.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
db_id | path | integer | yes | The extra database id. |
cancel | body | boolean | no | true = remove it when its paid months end (a full copy is taken first); false = keep it (withdraw the cancellation). Default: True. |
confirm | body | boolean | yes | Must be true. |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/extra-databases/db_id/cancel" \
-H "Authorization: Bearer $PBN_API_KEY"
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/extra-databases/db_id/cancel", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/extra-databases/db_id/cancel", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"id": 311,
"site_id": 12345,
"site_domain": "example.com",
"name": "site12345_shop",
"label": "shop",
"state": "ready",
"state_label": "Ready",
"read_only": false,
"size_bytes": 5242880,
"measured_at": "2026-09-24T06:00:00Z",
"paid_until": "2026-11-01",
"last_paid_day": "2026-10-31",
"in_grace": false,
"cancel_at_end": true,
"last_backup_at": "2026-09-24T03:10:00Z",
"restore_pending": false,
"error": "",
"created_at": "2026-09-24T09:00:00Z",
"manage_url": "https://app.pbn.ltd/extra-databases/site/12345/"
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized, validation_failed
Remove an extra database now
DELETE/api/v1/sites/{site_id}/extra-databases/{db_id}Scope extradb:write · destructive
Removes the database now. A full copy is taken first and kept for the days in removed_copy_kept_days (ask support to put it back). Needs "confirm": true and the database name typed exactly in "name". Paid months are not refunded.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
db_id | path | integer | yes | The extra database id. |
name | body | string | yes | The database name, typed exactly (e.g. site12345_shop) - the same check as the page. |
confirm | body | boolean | yes | Must be true. |
Example
curl -s -X DELETE "https://app.pbn.ltd/api/v1/sites/123/extra-databases/db_id" \
-H "Authorization: Bearer $PBN_API_KEY"
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.delete("https://app.pbn.ltd/api/v1/sites/123/extra-databases/db_id", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/extra-databases/db_id", {
method: "DELETE",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
202
{
"data": {
"id": 311,
"site_id": 12345,
"site_domain": "example.com",
"name": "site12345_shop",
"label": "shop",
"state": "deleting",
"state_label": "Being removed",
"read_only": false,
"size_bytes": 5242880,
"measured_at": "2026-09-24T06:00:00Z",
"paid_until": "2026-11-01",
"last_paid_day": "2026-10-31",
"in_grace": false,
"cancel_at_end": false,
"last_backup_at": "2026-09-24T03:10:00Z",
"restore_pending": false,
"error": "",
"created_at": "2026-09-24T09:00:00Z",
"manage_url": "https://app.pbn.ltd/extra-databases/site/12345/"
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized, validation_failed
Restore an extra database from a backup
POST/api/v1/sites/{site_id}/extra-databases/{db_id}/restoreScope extradb:write · destructive
Puts the database back as it was at the chosen daily backup: everything written since is replaced. A copy of the database as it is now is taken first. Usually done within a few minutes (you get an e-mail). Needs "confirm": true and the database name typed exactly in "name".
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
db_id | path | integer | yes | The extra database id. |
backup | body | string | yes | A restore point id from extradb.get (restore_points[].id). |
name | body | string | yes | The database name, typed exactly (e.g. site12345_shop) - the same check as the page. |
confirm | body | boolean | yes | Must be true. |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/extra-databases/db_id/restore" \
-H "Authorization: Bearer $PBN_API_KEY"
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/extra-databases/db_id/restore", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/extra-databases/db_id/restore", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
202
{
"data": {
"id": 311,
"site_id": 12345,
"site_domain": "example.com",
"name": "site12345_shop",
"label": "shop",
"state": "ready",
"state_label": "Ready",
"read_only": false,
"size_bytes": 5242880,
"measured_at": "2026-09-24T06:00:00Z",
"paid_until": "2026-11-01",
"last_paid_day": "2026-10-31",
"in_grace": false,
"cancel_at_end": false,
"last_backup_at": "2026-09-24T03:10:00Z",
"restore_pending": true,
"error": "",
"created_at": "2026-09-24T09:00:00Z",
"manage_url": "https://app.pbn.ltd/extra-databases/site/12345/"
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized, validation_failed