PBN.LTD API docs
View as Markdown

Footprint checker

What publicly joins your sites together - shared addresses, nameservers, tracking codes, themes, icons, footers, links, mail records and certificate history - with a fix for each.

Footprint checker: plan, usage and latest score

GET/api/v1/footprint

Scope footprint:read

Your plan, how often checks run, how many external sites you may hold and how many you are using, and the score from the most recent check.

Example

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

Response

200

{
  "data": {
    "active": true,
    "plan": "Monthly",
    "cadence": "monthly",
    "paid_until": "2026-10-31",
    "external_sites": {
      "used": 8,
      "limit": 25
    },
    "manual_runs_this_month": {
      "used": 2,
      "limit": 20
    },
    "hosted_sites": 12,
    "latest": {
      "id": 412,
      "name": "Your whole account",
      "scope": "account",
      "state": "done",
      "score": 72,
      "grade": "C",
      "findings": 5,
      "sites": 12,
      "external_sites": 3,
      "finished_at": "2026-09-22T08:00:00Z",
      "source": "auto"
    }
  }
}

Errors: rate_limited, scope_missing, unauthorized

List footprint reports

GET/api/v1/footprint/reports

Scope footprint:read

Every finished report, newest first.

Parameters

NameInTypeRequiredDescription
limitqueryintegernoItems per page. Default: 25.
cursorquerystringnonext_cursor of the previous page.

Example

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

Response

200

{
  "data": [
    {
      "id": 412,
      "name": "Your whole account",
      "scope": "account",
      "state": "done",
      "score": 72,
      "grade": "C",
      "findings": 5,
      "sites": 12,
      "external_sites": 3,
      "finished_at": "2026-09-22T08:00:00Z",
      "source": "auto"
    }
  ],
  "next_cursor": null,
  "has_more": false
}

Errors: rate_limited, scope_missing, unauthorized

One report and its findings

GET/api/v1/footprint/reports/{run_id}

Scope footprint:read

The report with every finding: what it is, what it means, which of your sites it affects and how to fix it. A finding marked owner: platform is one we are handling for you - there is nothing for you to do about it.

Parameters

NameInTypeRequiredDescription
run_idpathintegeryesThe report id.

Example

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

Response

200

{
  "data": {
    "id": 412,
    "name": "Your whole account",
    "scope": "account",
    "state": "done",
    "score": 72,
    "grade": "C",
    "findings": [
      {
        "code": "shared_ga",
        "severity": "critical",
        "owner": "customer",
        "title": "4 of your sites carry the same analytics account",
        "what_it_means": "This identifier sits in the page source of every one of these sites, where anybody can read it ...",
        "how_to_fix": "Give each site its own analytics account ...",
        "domains": [
          "one.example",
          "two.example"
        ],
        "count": 4,
        "is_platform": false,
        "is_good": false
      }
    ],
    "sites": 12,
    "external_sites": 3,
    "finished_at": "2026-09-22T08:00:00Z",
    "source": "auto"
  }
}

Errors: not_found, rate_limited, scope_missing, unauthorized

Run a check now

POST/api/v1/footprint/check

Scope footprint:write

Queues a check. Results are usually ready within a few minutes - poll /footprint/reports/{id}. Uses one of your monthly "Check now" runs; the automatic checks never count against that.

Parameters

NameInTypeRequiredDescription
scopebodystring (one of: site, group, account, custom)noWhat to check: one site, a site group, the whole account, or a selection of domains you own. Default: account.
scope_idbodyintegernoThe site id or site group id, for those scopes.
domainsbodyarraynoFor scope=custom: domains on your account.

Example

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

Response

202

{
  "data": {
    "queued": true,
    "report_id": 413
  }
}

Errors: payment_required, rate_limited, scope_missing, unauthorized, validation_failed

Sites you host elsewhere

GET/api/v1/footprint/external-sites

Scope footprint:read

The sites hosted elsewhere that are included in your checks. Sites hosted with us are always included and never use a slot.

Example

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

Response

200

{
  "data": {
    "items": [
      {
        "id": 9,
        "domain": "example.com",
        "group": "",
        "added": "2026-09-01"
      }
    ],
    "used": 8,
    "limit": 25
  }
}

Errors: rate_limited, scope_missing, unauthorized

Include a site hosted elsewhere

POST/api/v1/footprint/external-sites

Scope footprint:write

Adds a site hosted elsewhere. If you have no slots left this answers 422 naming the pack that would cover it - nothing is bought automatically.

Parameters

NameInTypeRequiredDescription
domainbodystringyese.g. example.com
groupbodystringnoYour own grouping for it.

Example

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

Response

201

{
  "data": {
    "id": 9,
    "domain": "example.com"
  }
}

Errors: payment_required, rate_limited, scope_missing, unauthorized, validation_failed

Stop including a site hosted elsewhere

DELETE/api/v1/footprint/external-sites/{site_id}

Scope footprint:write

Removes it and frees its slot straight away.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe id from /footprint/external-sites.

Example

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

Response

200

{
  "data": {
    "removed": true,
    "id": 9
  }
}

Errors: not_found, rate_limited, scope_missing, unauthorized