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/footprintScope 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/reportsScope footprint:read
Every finished report, newest first.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit | query | integer | no | Items per page. Default: 25. |
cursor | query | string | no | next_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
| Name | In | Type | Required | Description |
|---|---|---|---|---|
run_id | path | integer | yes | The 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/checkScope 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
| Name | In | Type | Required | Description |
|---|---|---|---|---|
scope | body | string (one of: site, group, account, custom) | no | What to check: one site, a site group, the whole account, or a selection of domains you own. Default: account. |
scope_id | body | integer | no | The site id or site group id, for those scopes. |
domains | body | array | no | For 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-sitesScope 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-sitesScope 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
| Name | In | Type | Required | Description |
|---|---|---|---|---|
domain | body | string | yes | e.g. example.com |
group | body | string | no | Your 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
| Name | In | Type | Required | Description |
|---|---|---|---|---|
site_id | path | integer | yes | The 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