# 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 /client-reports` · scope `reports:read`

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

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/client-reports" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
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())
```

```javascript
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`

```json
{
  "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 /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

```bash
curl -s "https://app.pbn.ltd/api/v1/client-reports/clients" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
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())
```

```javascript
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`

```json
{
  "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 /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

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `client_id` | query | integer | no | Only reports for this client. |
| `limit` | query | integer | no | Items per page. Default: `25`. |
| `cursor` | query | string | no | next_cursor of the previous page. |

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/client-reports/reports" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
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())
```

```javascript
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`

```json
{
  "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 /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

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `report_id` | path | integer | yes | The report id. |

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/client-reports/reports/report_id" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
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())
```

```javascript
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`

```json
{
  "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 /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

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `client_id` | body | integer | yes | The client. |
| `send` | body | boolean | no | Also e-mail it to that client's addresses. Default: `False`. |

### Example

```bash
curl -s -X POST "https://app.pbn.ltd/api/v1/client-reports/reports" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
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())
```

```javascript
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`

```json
{
  "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 /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

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `report_id` | path | integer | yes | The report id. |

### Example

```bash
curl -s -X POST "https://app.pbn.ltd/api/v1/client-reports/reports/report_id/revoke" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
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())
```

```javascript
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`

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

Errors: `not_found`, `rate_limited`, `scope_missing`, `unauthorized`
