PBN.LTD API docs
View as Markdown

Sites

Every site type: list, search, create, change, delete; status, nameservers, CDN, SEO.

List and search sites

GET/api/v1/sites

Scope sites:read

The same search, filters and order as the sites list in the panel. total is the number of matching sites. A key restricted to some sites only sees those.

Parameters

NameInTypeRequiredDescription
searchquerystringnoPart of the domain or name (www. is ignored).
statequerystring (one of: ok, waiting, activating, working, error, frozen, notinstalled)nook = live, waiting = waiting for DNS, activating = activating CDN, working = being installed/changed, error, frozen = paused, notinstalled.
typequerystring (one of: Wordpress, Static HTML, PHP hosting, Joomla, Drupal, PrestaShop, OpenCart, Grav, MediaWiki, mismatch)noSite type; "mismatch" = the files run a different platform than the site type.
groupquerystringnoGroup id, or "none" for sites in no group.
cdnquerystringnoCDN name, e.g. Cloudflare, BunnyCDN, KeyCDN, CDN77.COM, Gcore, CloudFront.
php_versionquerystringnoPHP version value, e.g. "PHP 8.3".
indexedquerystring (one of: yes, no, pending)noGoogle indexation state.
onlinequerystring (one of: online, offline)noThe online badge: offline = a confirmed problem.
sortquerystring (one of: newest, oldest, name, name_desc, domain, domain_desc)noOrder of the list. Default: newest.
limitqueryintegernoItems per page (1-200). Default: 50.
cursorquerystringnoThe next_cursor value of the previous page. Omit for the first page.

Example

curl -s "https://app.pbn.ltd/api/v1/sites?search=blog&state=ok&limit=20" \
  -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?search=blog&state=ok&limit=20", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites?search=blog&state=ok&limit=20", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": [
    {
      "id": 123,
      "name": "example",
      "domain": "example.com",
      "type": "Wordpress",
      "state": "Ok",
      "state_group": "ok",
      "url": "https://example.com",
      "created_at": "2026-01-05T12:00:00Z",
      "group": {
        "id": 7,
        "name": "Travel"
      },
      "cdn": "Cloudflare",
      "php_version": "PHP 8.3",
      "use_https": true,
      "use_www": false,
      "frozen": false,
      "switched_off_by_staff": false,
      "suspended": false,
      "pending_delete": false,
      "main_cloud": false,
      "indexed": "yes",
      "last_indexed_at": "2026-09-10T03:00:00Z",
      "last_not_indexed_at": null,
      "domain_expires_at": "2027-03-01T00:00:00Z",
      "seo": {
        "trust_flow": 12,
        "citation_flow": 20,
        "backlinks": 340,
        "referring_domains": 41,
        "trust_flow_change": 1,
        "citation_flow_change": 0,
        "backlinks_change": 12,
        "referring_domains_change": 2,
        "measured_at": "2026-09-15T02:00:00Z"
      },
      "platform": {
        "type": "Wordpress",
        "label": "WordPress",
        "detected": "wordpress",
        "mismatch": null,
        "checked_at": "2026-09-19T04:00:00Z"
      },
      "online": {
        "status": "online",
        "label": "Online",
        "reason": null,
        "since": null,
        "checked_at": "2026-09-19T20:00:00Z",
        "text": "Homepage answered normally (last check ...)."
      }
    }
  ],
  "next_cursor": null,
  "has_more": false
}

Errors: rate_limited, scope_missing, unauthorized

Get one site

GET/api/v1/sites/{site_id}

Scope sites:read

Everything the site page shows: state, URL, settings, why it is paused (freeze.reasons), nameserver status (current vs required, pointed, autopilot), CDN, SEO, indexation, platform, online status and what can be done with it right now (capabilities).

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).

Example

curl -s "https://app.pbn.ltd/api/v1/sites/123" \
  -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", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "id": 123,
    "name": "example",
    "domain": "example.com",
    "type": "Wordpress",
    "state": "Ok",
    "state_group": "ok",
    "url": "https://example.com",
    "created_at": "2026-01-05T12:00:00Z",
    "group": {
      "id": 7,
      "name": "Travel"
    },
    "cdn": "Cloudflare",
    "php_version": "PHP 8.3",
    "use_https": true,
    "use_www": false,
    "frozen": false,
    "switched_off_by_staff": false,
    "suspended": false,
    "pending_delete": false,
    "main_cloud": false,
    "indexed": "yes",
    "last_indexed_at": "2026-09-10T03:00:00Z",
    "last_not_indexed_at": null,
    "domain_expires_at": "2027-03-01T00:00:00Z",
    "seo": {
      "trust_flow": 12,
      "citation_flow": 20,
      "backlinks": 340,
      "referring_domains": 41,
      "trust_flow_change": 1,
      "citation_flow_change": 0,
      "backlinks_change": 12,
      "referring_domains_change": 2,
      "measured_at": "2026-09-15T02:00:00Z"
    },
    "platform": {
      "type": "Wordpress",
      "label": "WordPress",
      "detected": "wordpress",
      "mismatch": null,
      "checked_at": "2026-09-19T04:00:00Z"
    },
    "online": {
      "status": "online",
      "label": "Online",
      "reason": null,
      "since": null,
      "checked_at": "2026-09-19T20:00:00Z",
      "text": "Homepage answered normally (last check ...)."
    },
    "title": "My blog",
    "subtitle": null,
    "login_url": "wp-login.php",
    "admin_email": "[email protected]",
    "autoupdate_wordpress": true,
    "ssl_mode": "full",
    "mailbox_requested": false,
    "freeze": {
      "frozen": false,
      "reasons": [],
      "temporary_unfreeze": null
    },
    "nameservers": {
      "current": [
        "ada.ns.cloudflare.com",
        "bob.ns.cloudflare.com"
      ],
      "required": [
        "ada.ns.cloudflare.com",
        "bob.ns.cloudflare.com"
      ],
      "pointed": true,
      "main_cloud": false,
      "zone_expired": false,
      "dns_provider": "Cloudflare",
      "checked_at": "2026-09-19T13:25:00Z",
      "autopilot": null
    },
    "cdn_detail": {
      "provider": "Cloudflare",
      "https": true,
      "www": false,
      "own_account": false,
      "https_only": true
    },
    "capabilities": {
      "edit": true,
      "delete": true,
      "backup": true,
      "restore": true,
      "reinstall": true,
      "purge_cache": true,
      "admin_login": true,
      "files": true,
      "error_log": true,
      "dns_records": true
    },
    "backups_count": 4,
    "panel_url": "https://app.pbn.ltd/sites/123",
    "updated_at": "2026-09-19T10:00:00Z"
  }
}

Errors: not_found, rate_limited, scope_missing, unauthorized

Fields and choices for a new site

GET/api/v1/sites/options

Scope sites:write

The fields the Create site form accepts for this account right now, with their choices (site types, CDNs, PHP versions, templates, blueprints, groups...) and defaults. Build a create screen from it; every field name here is accepted by POST /sites (unknown ones go in form_fields).

Example

curl -s "https://app.pbn.ltd/api/v1/sites/options" \
  -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/options", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/options", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "fields": [
      {
        "name": "type",
        "form_field": "type",
        "label": "Type",
        "required": true,
        "help": null,
        "kind": "typedchoice",
        "choices": [
          {
            "value": "Wordpress",
            "label": "WordPress"
          }
        ],
        "default": "Wordpress"
      }
    ]
  }
}

Errors: rate_limited, scope_missing, unauthorized

Create a site (any type)

POST/api/v1/sites

Scope sites:write · returns a job

Creates a site exactly as the Add new site form does - same validation, same slot limit, same checks - for every type: WordPress, Static HTML, PHP hosting, Joomla, Drupal, PrestaShop, OpenCart, Grav and MediaWiki. Installation runs in the background: follow the returned job (GET /jobs/{job_id}) or the site state. Once installed, point the domain at nameservers.required from GET /sites/{site_id}.

Parameters

NameInTypeRequiredDescription
typebodystring (one of: Wordpress, Static HTML, PHP hosting, Joomla, Drupal, PrestaShop, OpenCart, Grav, MediaWiki)yesSite type.
namebodystringyesA unique short name (letters, digits, dashes).
domainbodystringyesThe domain (or subdomain of one of your sites).
cdnbodystringnoCDN (see GET /sites/options for the choices open to you).
php_versionbodystringnoPHP version value, e.g. "PHP 8.3" (see GET /sites/options).
use_httpsbodybooleannoServe over HTTPS.
use_wwwbodybooleannoUse www. as the primary host.
titlebodystringnoSite title (WordPress and the ready-installed applications).
subtitlebodystringnoTagline (WordPress).
admin_emailbodystringnoAdministrator e-mail (WordPress and the applications).
login_urlbodystringnoWordPress login path (default wp-login.php).
template_idbodyintegernoWordPress template id (omit for random).
blueprint_idbodyintegernoDeploy from one of your blueprints (WordPress).
group_idbodyintegernoPut the site in this group.
create_mailboxbodybooleannoCreate an e-mail account for the domain.
autoupdate_wordpressbodybooleannoAuto-update WordPress core and plugins.
form_fieldsbodyobjectnoAny other Create site form field by its form name (see form_field in GET /sites/options), e.g. the WordPress theme/plugin pickers.

Example

curl -s -X POST "https://app.pbn.ltd/api/v1/sites" \
  -H "Authorization: Bearer $PBN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "Wordpress", "name": "myblog", "domain": "myblog-example.com", "cdn": "Cloudflare", "use_https": true, "title": "My blog", "subtitle": "Notes from the workshop", "admin_email": "[email protected]", "form_fields": {"feedback_email": "[email protected]"}}'
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites", headers=headers, json={"type": "Wordpress", "name": "myblog", "domain": "myblog-example.com", "cdn": "Cloudflare", "use_https": True, "title": "My blog", "subtitle": "Notes from the workshop", "admin_email": "[email protected]", "form_fields": {"feedback_email": "[email protected]"}}, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`, "Content-Type": "application/json"},
  body: JSON.stringify({"type": "Wordpress", "name": "myblog", "domain": "myblog-example.com", "cdn": "Cloudflare", "use_https": true, "title": "My blog", "subtitle": "Notes from the workshop", "admin_email": "[email protected]", "form_fields": {"feedback_email": "[email protected]"}})
});
console.log(res.status, await res.json());

Response

202

{
  "data": {
    "id": 124,
    "domain": "new-example.com",
    "state": "New",
    "...": "(the site, as GET /sites/{site_id})"
  },
  "job": {
    "id": "job_4f1c0a9e2b7d6c5a3e10",
    "kind": "...",
    "status": "running",
    "url": "https://app.pbn.ltd/api/v1/jobs/job_4f1c0a9e2b7d6c5a3e10"
  }
}

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

Fields and choices to edit a site

GET/api/v1/sites/{site_id}/options

Scope sites:write

The fields the Edit site form offers for THIS site right now (they depend on type, CDN and state), with choices and current values.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).

Example

curl -s "https://app.pbn.ltd/api/v1/sites/123/options" \
  -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/options", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/options", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "fields": [
      {
        "name": "php_version",
        "label": "Php version",
        "required": true,
        "choices": [
          {
            "value": "PHP 8.3",
            "label": "PHP 8.3"
          }
        ],
        "default": "PHP 8.3"
      }
    ]
  }
}

Errors: not_found, rate_limited, scope_missing, unauthorized

Change site settings

PATCH/api/v1/sites/{site_id}

Scope sites:write

Change any setting the Edit site form offers: name, domain, PHP version, HTTPS, www, group, admin e-mail, login URL, auto-updates, mailbox. Send only what changes. Changes that touch the server (domain, PHP, HTTPS/www) run in the background: a job is returned then.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
namebodystringnoA unique short name (letters, digits, dashes).
domainbodystringnoThe domain (or subdomain of one of your sites).
php_versionbodystringnoPHP version value, e.g. "PHP 8.3" (see GET /sites/options).
use_httpsbodybooleannoServe over HTTPS.
use_wwwbodybooleannoUse www. as the primary host.
admin_emailbodystringnoAdministrator e-mail (WordPress and the applications).
login_urlbodystringnoWordPress login path (default wp-login.php).
group_idbodyintegernoPut the site in this group.
create_mailboxbodybooleannoCreate an e-mail account for the domain.
autoupdate_wordpressbodybooleannoAuto-update WordPress core and plugins.
form_fieldsbodyobjectnoAny other Create site form field by its form name (see form_field in GET /sites/options), e.g. the WordPress theme/plugin pickers.
ssl_modebodystring (one of: full, flexible)noCloudflare SSL mode (only offered when the site has its own address records).

Example

curl -s -X PATCH "https://app.pbn.ltd/api/v1/sites/123" \
  -H "Authorization: Bearer $PBN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"php_version": "PHP 8.3", "use_www": true}'
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.patch("https://app.pbn.ltd/api/v1/sites/123", headers=headers, json={"php_version": "PHP 8.3", "use_www": True}, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123", {
  method: "PATCH",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`, "Content-Type": "application/json"},
  body: JSON.stringify({"php_version": "PHP 8.3", "use_www": true})
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "id": 123,
    "name": "example",
    "domain": "example.com",
    "type": "Wordpress",
    "state": "Ok",
    "state_group": "ok",
    "url": "https://example.com",
    "created_at": "2026-01-05T12:00:00Z",
    "group": {
      "id": 7,
      "name": "Travel"
    },
    "cdn": "Cloudflare",
    "php_version": "PHP 8.3",
    "use_https": true,
    "use_www": false,
    "frozen": false,
    "switched_off_by_staff": false,
    "suspended": false,
    "pending_delete": false,
    "main_cloud": false,
    "indexed": "yes",
    "last_indexed_at": "2026-09-10T03:00:00Z",
    "last_not_indexed_at": null,
    "domain_expires_at": "2027-03-01T00:00:00Z",
    "seo": {
      "trust_flow": 12,
      "citation_flow": 20,
      "backlinks": 340,
      "referring_domains": 41,
      "trust_flow_change": 1,
      "citation_flow_change": 0,
      "backlinks_change": 12,
      "referring_domains_change": 2,
      "measured_at": "2026-09-15T02:00:00Z"
    },
    "platform": {
      "type": "Wordpress",
      "label": "WordPress",
      "detected": "wordpress",
      "mismatch": null,
      "checked_at": "2026-09-19T04:00:00Z"
    },
    "online": {
      "status": "online",
      "label": "Online",
      "reason": null,
      "since": null,
      "checked_at": "2026-09-19T20:00:00Z",
      "text": "Homepage answered normally (last check ...)."
    }
  }
}

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

Delete a site

DELETE/api/v1/sites/{site_id}

Scope sites:delete · destructive

Deletes the site for good (files, database, DNS zone, CDN), exactly like Delete site in the panel. Refused while the site is busy or paused for non-payment/malware, while it has subdomain sites, or while a blueprint is being made from it. Runs in the background: a job is returned.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).

Example

curl -s -X DELETE "https://app.pbn.ltd/api/v1/sites/123" \
  -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", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123", {
  method: "DELETE",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

202

{
  "data": {
    "id": 123,
    "deleting": true
  }
}

Errors: conflict, not_found, rate_limited, scope_missing, unauthorized