Site tools
Cache purge, one-click admin login, reinstall, health checks, Site Cleaner, usage.
Change the PHP version
PUT/api/v1/sites/{site_id}/php-versionScope sites:write
Shortcut for PATCH /sites/{site_id} with php_version. The web server is reconfigured in the background (a job is returned).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
php_version | body | string | yes | e.g. "PHP 8.3" (see GET /sites/{site_id}/options) |
Example
curl -s -X PUT "https://app.pbn.ltd/api/v1/sites/123/php-version" \
-H "Authorization: Bearer $PBN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"php_version": "PHP 8.3"}'
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.put("https://app.pbn.ltd/api/v1/sites/123/php-version", headers=headers, json={"php_version": "PHP 8.3"}, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/php-version", {
method: "PUT",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`, "Content-Type": "application/json"},
body: JSON.stringify({"php_version": "PHP 8.3"})
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"php_version": "PHP 8.3",
"state": "Creating webserver config"
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized
Disk, database and file usage
GET/api/v1/sites/{site_id}/usageScope sites:read
The Usage & add-ons tab: disk, database and file-count use against the limits (plan + add-ons), what is over, CDN bandwidth for metered CDNs, and the add-ons on the site. Numbers are measured every few hours; POST /sites/{site_id}/usage/refresh measures now.
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/usage" \
-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/usage", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/usage", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"site_id": 123,
"state": "Ok",
"frozen": false,
"over": [],
"disk": {
"used_mib": 310,
"limit_mib": 1024,
"included_mib": 1024,
"addon_mib": 0,
"percent": 30,
"over": false,
"measured_at": "2026-09-19T18:00:00+00:00"
},
"db": {
"used_mib": 40,
"limit_mib": 500
},
"inodes": {
"used": 8200,
"limit": 50000
},
"bandwidth": null,
"addons": [],
"busy": false
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized
Measure usage now
POST/api/v1/sites/{site_id}/usage/refreshScope sites:write
Starts a fresh measurement of disk, database and files (the usage tab's "Check now"). Returns the current figures with busy=true; poll GET /sites/{site_id}/usage until busy is false (seconds). Same throttles as the panel: one measurement per site every few minutes.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/usage/refresh" \
-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/usage/refresh", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/usage/refresh", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"busy": true,
"disk": {
"used_mib": 310
}
}
}
Errors: conflict, not_found, rate_limited, scope_missing, unauthorized, unavailable
Purge the CDN cache
POST/api/v1/sites/{site_id}/purge-cacheScope sites:write
Clears the CDN cache of both hostnames (as Purge CDN cache in the panel). The site must be live.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/purge-cache" \
-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/purge-cache", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/purge-cache", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"purged": true
}
}
Errors: conflict, not_found, rate_limited, scope_missing, unauthorized
Reinstall from scratch
POST/api/v1/sites/{site_id}/reinstallScope sites:write · destructive
Installs the site again from scratch (as Reinstall in the panel) - the current files and database are replaced. Refused while the site is busy, switched off or being deleted.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
confirm | body | boolean | yes | Must be true: this action overwrites the site. |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/reinstall" \
-H "Authorization: Bearer $PBN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"confirm": true}'
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/reinstall", headers=headers, json={"confirm": True}, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/reinstall", {
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
{
"data": {
"reinstalling": true
}
}
Errors: conflict, not_found, rate_limited, scope_missing, unauthorized, validation_failed
One-click admin login link
POST/api/v1/sites/{site_id}/admin-loginScope sites:login
A single-use login link to the site's admin (WordPress wp-admin, Joomla, Drupal, PrestaShop, OpenCart, Grav, MediaWiki), valid for 60 seconds - the panel's one-click login. Open it in a browser; do not store it. Not for Static HTML / PHP hosting sites.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/admin-login" \
-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/admin-login", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/admin-login", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"url": "https://example.com/?ltoken=0f3c...",
"expires_in_seconds": 60,
"single_use": true
}
}
Errors: conflict, not_found, rate_limited, scope_missing, unauthorized
Health status
GET/api/v1/sites/{site_id}/healthScope sites:read
The online badge (online / offline / slow / paused... with the reason) and, for application sites, the last integrity check (V1 config lines, admin account, login health, core files).
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/health" \
-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/health", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/health", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"site_id": 123,
"online": {
"status": "online",
"label": "Online"
},
"integrity": {
"checked": "19 Sep 2026 20:00 UTC",
"status": "ok",
"findings": [],
"problems": 0
}
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized
Re-check if the site is up
POST/api/v1/sites/{site_id}/health/recheckScope sites:write
Probes the home page again now (same probe and recording as the platform's monitor) and asks the server why if it fails. Asynchronous: follow the job; its result has the outcome. At most 10 an hour.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/health/recheck" \
-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/health/recheck", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/health/recheck", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
202
{
"data": {
"started": true
}
}
Errors: conflict, not_found, rate_limited, scope_missing, unauthorized
Integrity check now
POST/api/v1/sites/{site_id}/health/integrity-checkScope sites:write
Checks the application now (WordPress, Joomla, Drupal, PrestaShop, OpenCart, Grav, MediaWiki): V1's config lines, the site URL, the admin account, one-click login health and core file checksums. Changes nothing. Takes a few seconds.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/health/integrity-check" \
-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/health/integrity-check", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/health/integrity-check", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"checked": "20 Sep 2026 08:00 UTC",
"status": "ok",
"findings": [],
"problems": 0,
"message": "No problems found."
}
}
Errors: conflict, not_found, rate_limited, scope_missing, unauthorized
Repair the site
POST/api/v1/sites/{site_id}/health/repairScope sites:write
"Repair site": checks, repairs everything repairable (backups first, on the server), checks again. You get an e-mail listing what was repaired.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/health/repair" \
-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/health/repair", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/health/repair", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"status": "ok",
"fixed": [
"WordPress core files restored"
],
"message": "Repaired."
}
}
Errors: conflict, not_found, rate_limited, scope_missing, unauthorized
Switch a paused site on for cleanup
POST/api/v1/sites/{site_id}/temporary-unfreezeScope sites:write
A site paused ONLY for a usage limit (disk / database / files) comes back for 30 minutes so you can clean it up (3 times per site per day). At the end it is paused again only if still over the limit. freeze.temporary_unfreeze in GET /sites/{site_id} says if it is available and why not.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/temporary-unfreeze" \
-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/temporary-unfreeze", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/temporary-unfreeze", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"active": true,
"ends_at": "2026-09-20T09:30:00+00:00",
"message": "example.com is coming back online for 30 minutes..."
}
}
Errors: conflict, not_found, rate_limited, scope_missing, unauthorized
Site Cleaner status
GET/api/v1/sites/{site_id}/cleanerScope sites:read
Site Cleaner for this site: whether the add-on is active, the last scan (what can be removed and how much it saves), the running job, recent jobs (with undo_available) and the schedule.
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/cleaner" \
-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/cleaner", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/cleaner", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"site_id": 123,
"cms": "wordpress",
"usable": true,
"why_not": null,
"addon_active": true,
"schedule": "off",
"last_scan": {
"items": [
{
"id": "plugin:hello-dolly",
"cat": "plugins",
"name": "Hello Dolly",
"mb": 0.1,
"default": true
}
],
"at": "2026-09-19T10:00:00Z"
},
"running": null,
"recent_jobs": [],
"buy_url": "https://app.pbn.ltd/site-cleaner/"
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized
Scan, clean or undo
POST/api/v1/sites/{site_id}/cleaner/{action}Scope sites:write
Runs Site Cleaner. scan lists unused plugins/themes, junk files and database clutter (free). clean removes the selected items after taking a backup and checks the site afterwards - it needs the Site Cleaner add-on (402 addon_required otherwise). undo puts a clean back from its backup.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
action | path | string (one of: scan, clean, undo) | yes | scan (free) / clean (needs the Site Cleaner add-on) / undo a clean. |
items | body | array | no | clean: ids from last_scan.items to remove (omit = the default selection). |
job_id | body | integer | no | undo: the id of the clean job to undo. |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/cleaner/scan" \
-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/cleaner/scan", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/cleaner/scan", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
202
{
"data": {
"cleaner_job": {
"id": 991,
"kind": "scan",
"state": "queued"
}
}
}
Errors: conflict, not_found, payment_required, rate_limited, scope_missing, unauthorized, validation_failed
Automatic cleaning
PUT/api/v1/sites/{site_id}/cleaner/scheduleScope sites:write
Sets automatic cleaning for the site (needs the add-on to actually clean).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The site id (see GET /sites). |
schedule | body | string (one of: off, daily, weekly, monthly) | yes | How often Site Cleaner runs by itself. |
Example
curl -s -X PUT "https://app.pbn.ltd/api/v1/sites/123/cleaner/schedule" \
-H "Authorization: Bearer $PBN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"schedule": "weekly"}'
import os
import requests
headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.put("https://app.pbn.ltd/api/v1/sites/123/cleaner/schedule", headers=headers, json={"schedule": "weekly"}, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/cleaner/schedule", {
method: "PUT",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`, "Content-Type": "application/json"},
body: JSON.stringify({"schedule": "weekly"})
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"schedule": "weekly",
"text": "weekly"
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized