PBN.LTD API docs
View as Markdown

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

Scope sites:write

Shortcut for PATCH /sites/{site_id} with php_version. The web server is reconfigured in the background (a job is returned).

Parameters

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

Scope 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

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

Scope 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

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

Scope sites:write

Clears the CDN cache of both hostnames (as Purge CDN cache in the panel). The site must be live.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe 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}/reinstall

Scope 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

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

Scope 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

NameInTypeRequiredDescription
site_idpathintegeryesThe 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}/health

Scope 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

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

Scope 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

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

Scope 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

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

Scope 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

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

Scope 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

NameInTypeRequiredDescription
site_idpathintegeryesThe 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}/cleaner

Scope 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

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

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
actionpathstring (one of: scan, clean, undo)yesscan (free) / clean (needs the Site Cleaner add-on) / undo a clean.
itemsbodyarraynoclean: ids from last_scan.items to remove (omit = the default selection).
job_idbodyintegernoundo: 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/schedule

Scope sites:write

Sets automatic cleaning for the site (needs the add-on to actually clean).

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
schedulebodystring (one of: off, daily, weekly, monthly)yesHow 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