PBN.LTD API docs
View as Markdown

Client reports

White-label reports for an agency's clients, built from the SEO tools the account already has.

Client reports: plan and usage

GET/api/v1/client-reports

Scope reports:read

Your plan, how many clients and reports it covers and how many you have used this month.

Example

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

Response

200

{
  "data": {
    "plan": "Reports Pro",
    "unlimited": false,
    "paid_until": "2026-10-31",
    "clients": 12,
    "clients_included": 25,
    "reports_used": 9,
    "reports_included": 60,
    "share_link_days": 60,
    "own_sender": true,
    "price_from": "12.00"
  }
}

Errors: rate_limited, scope_missing, unauthorized

List your clients

GET/api/v1/client-reports/clients

Scope reports:read

Every client you report on, what of theirs a report covers and when the next one is due.

Example

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

Response

200

{
  "data": [
    {
      "id": 5,
      "name": "Acme Plumbing",
      "domains": [
        "acmeplumbing.co.uk"
      ],
      "cadence": "monthly",
      "next_report_on": "2026-10-01",
      "reports": 7
    }
  ]
}

Errors: rate_limited, scope_missing, unauthorized

List the reports you have made

GET/api/v1/client-reports/reports

Scope reports:read

Every report made on this account, newest first. The share link itself is only returned by the single-report endpoint.

Parameters

NameInTypeRequiredDescription
client_idqueryintegernoOnly reports for this client.
limitqueryintegernoItems per page. Default: 25.
cursorquerystringnonext_cursor of the previous page.

Example

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

Response

200

{
  "data": [
    {
      "id": 88,
      "client": "Acme Plumbing",
      "period_start": "2026-08-22",
      "period_end": "2026-09-22",
      "share_live": true,
      "views": 3,
      "created_at": "2026-09-22T09:00:00Z"
    }
  ],
  "next_cursor": null,
  "has_more": false
}

Errors: rate_limited, scope_missing, unauthorized

One report

GET/api/v1/client-reports/reports/{report_id}

Scope reports:read

The whole report: every section as it was frozen when it was made, the plain "what changed" summary, and the share link while it is live.

Parameters

NameInTypeRequiredDescription
report_idpathintegeryesThe report id.

Example

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

Response

200

{
  "data": {
    "id": 88,
    "client": "Acme Plumbing",
    "period_start": "2026-08-22",
    "period_end": "2026-09-22",
    "sections": [
      "rank",
      "local",
      "index"
    ],
    "share_url": "https://app.pbn.ltd/client-reports/r/\u2026",
    "share_expires_at": "2026-10-22T09:00:00Z",
    "share_live": true,
    "views": 3,
    "summary": [
      {
        "good": true,
        "text": "In the top 10 went up from 11 to 14 (google rankings)."
      }
    ],
    "created_at": "2026-09-22T09:00:00Z"
  }
}

Errors: not_found, rate_limited, scope_missing, unauthorized

Make a report now

POST/api/v1/client-reports/reports

Scope reports:write

Builds the report from what the account has already collected - it never spends any of your search credits - and returns it with its share link.

Parameters

NameInTypeRequiredDescription
client_idbodyintegeryesThe client.
sendbodybooleannoAlso e-mail it to that client's addresses. Default: False.

Example

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

Response

201

{
  "data": {
    "id": 88,
    "client": "Acme Plumbing",
    "period_start": "2026-08-22",
    "period_end": "2026-09-22",
    "sections": [
      "rank",
      "local",
      "index"
    ],
    "share_url": "https://app.pbn.ltd/client-reports/r/\u2026",
    "share_expires_at": "2026-10-22T09:00:00Z",
    "share_live": true,
    "views": 3,
    "summary": [
      {
        "good": true,
        "text": "In the top 10 went up from 11 to 14 (google rankings)."
      }
    ],
    "created_at": "2026-09-22T09:00:00Z"
  }
}

Errors: payment_required, rate_limited, scope_missing, unauthorized, validation_failed

Turn a share link off

POST/api/v1/client-reports/reports/{report_id}/revoke

Scope reports:write · destructive

The link stops working at once for everybody who has it. The report itself is kept and you can turn the link back on from the panel.

Parameters

NameInTypeRequiredDescription
report_idpathintegeryesThe report id.

Example

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

Response

200

{
  "data": {
    "revoked": true,
    "id": 88
  }
}

Errors: not_found, rate_limited, scope_missing, unauthorized