PBN.LTD API docs
View as Markdown

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

Scope 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-databases

Scope 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

NameInTypeRequiredDescription
site_idpathintegeryesThe 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

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
db_idpathintegeryesThe 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-databases

Scope 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

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
labelbodystringyesThe 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}/cancel

Scope 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

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
db_idpathintegeryesThe extra database id.
cancelbodybooleannotrue = remove it when its paid months end (a full copy is taken first); false = keep it (withdraw the cancellation). Default: True.
confirmbodybooleanyesMust 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

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
db_idpathintegeryesThe extra database id.
namebodystringyesThe database name, typed exactly (e.g. site12345_shop) - the same check as the page.
confirmbodybooleanyesMust 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}/restore

Scope 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

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
db_idpathintegeryesThe extra database id.
backupbodystringyesA restore point id from extradb.get (restore_points[].id).
namebodystringyesThe database name, typed exactly (e.g. site12345_shop) - the same check as the page.
confirmbodybooleanyesMust 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