# Backlink monitor

Are the links you built still there? Watch individual links and whole backlink profiles.

## Backlink monitor: plan and usage

`GET /backlinks` · scope `backlinks:read`

The plan, how many links and profiles may be watched, profile refreshes a month included and used, the refreshes your chosen cadences plan for, and how many watched links are live, gone or changed.

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/backlinks" \
  -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/backlinks", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/backlinks", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "active": true,
    "plan": "Pro",
    "paid_until": "2026-10-31",
    "links": {
      "used": 380,
      "limit": 1000
    },
    "domains": {
      "used": 7,
      "limit": 10
    },
    "refreshes_this_month": {
      "used": 22,
      "limit": 60,
      "planned": 30
    },
    "watched_links": {
      "live": 350,
      "gone": 18,
      "changed": 6,
      "pending": 4,
      "errors": 2
    }
  }
}
```

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

## List watched links

`GET /backlinks/links` · scope `backlinks:read`

Every link you watch, problems first.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `status` | query | string (one of: live, lost, changed, pending, error, problem) | no | Only links in this state. "problem" means gone or changed. |
| `host` | query | string | no | Only links from or to this domain (www. ignored). |
| `search` | query | string | no | Part of an address or an anchor. |
| `limit` | query | integer | no | Items per page. Default: `50`. |
| `cursor` | query | string | no | next_cursor of the previous page. |

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/backlinks/links" \
  -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/backlinks/links", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/backlinks/links", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": [
    {
      "id": 4120,
      "source_url": "https://blog.example.com/post/",
      "target_url": "https://mysite.com/page/",
      "status": "live",
      "problem": "",
      "anchor_wanted": "best widgets",
      "anchor_found": "best widgets",
      "rel": "",
      "dofollow": true,
      "http_status": 200,
      "frequency": "weekly",
      "last_checked_at": "2026-09-22T10:00:00Z",
      "last_change_at": null,
      "first_seen_at": "2026-08-01T10:00:00Z",
      "checks": 8,
      "hosted_site_id": null
    }
  ],
  "next_cursor": null,
  "has_more": false
}
```

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

## One watched link and its history

`GET /backlinks/links/{link_id}` · scope `backlinks:read`

The link and up to 100 of its most recent checks.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `link_id` | path | integer | yes | The watched link id. |

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/backlinks/links/link_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/backlinks/links/link_id", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/backlinks/links/link_id", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "id": 4120,
    "source_url": "https://blog.example.com/post/",
    "target_url": "https://mysite.com/page/",
    "status": "live",
    "problem": "",
    "anchor_wanted": "best widgets",
    "anchor_found": "best widgets",
    "rel": "",
    "dofollow": true,
    "http_status": 200,
    "frequency": "weekly",
    "last_checked_at": "2026-09-22T10:00:00Z",
    "last_change_at": null,
    "first_seen_at": "2026-08-01T10:00:00Z",
    "checks": 8,
    "hosted_site_id": null,
    "history": [
      {
        "checked_at": "2026-09-22T10:00:00Z",
        "status": "live",
        "changed": false,
        "anchor": "best widgets",
        "rel": "",
        "dofollow": true,
        "http_status": 200
      }
    ]
  }
}
```

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

## Watch links

`POST /backlinks/links` · scope `backlinks:write`

Adds links with the panel's own rules: anything past the plan's link limit is not added (see not_added). Checking a link costs nothing - we fetch the page ourselves.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `links` | body | array | yes | Up to 2,000 objects: {"source_url": "...", "target_url": "...", "anchor": "optional expected anchor text"}. |
| `frequency` | body | string (one of: daily, weekly, monthly) | no | How often the link is checked. Checking links is included in every plan. Default: `weekly`. |

### Example

```bash
curl -s -X POST "https://app.pbn.ltd/api/v1/backlinks/links" \
  -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/backlinks/links", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/backlinks/links", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`201`

```json
{
  "data": {
    "added": [
      {
        "id": 4120,
        "source_url": "https://blog.example.com/post/",
        "target_url": "https://mysite.com/page/",
        "status": "live",
        "problem": "",
        "anchor_wanted": "best widgets",
        "anchor_found": "best widgets",
        "rel": "",
        "dofollow": true,
        "http_status": 200,
        "frequency": "weekly",
        "last_checked_at": "2026-09-22T10:00:00Z",
        "last_change_at": null,
        "first_seen_at": "2026-08-01T10:00:00Z",
        "checks": 8,
        "hosted_site_id": null
      }
    ],
    "already_watched": 0,
    "invalid": [
      {
        "url": "not a url",
        "reason": "..."
      }
    ],
    "not_added": {
      "count": 0,
      "reason": null
    }
  }
}
```

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

## Change how often a link is checked

`PATCH /backlinks/links/{link_id}` · scope `backlinks:write`



### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `link_id` | path | integer | yes | The watched link id. |
| `frequency` | body | string (one of: daily, weekly, monthly) | no | How often the link is checked. Checking links is included in every plan. Default: `weekly`. |

### Example

```bash
curl -s -X PATCH "https://app.pbn.ltd/api/v1/backlinks/links/link_id" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.patch("https://app.pbn.ltd/api/v1/backlinks/links/link_id", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/backlinks/links/link_id", {
  method: "PATCH",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "id": 4120,
    "source_url": "https://blog.example.com/post/",
    "target_url": "https://mysite.com/page/",
    "status": "live",
    "problem": "",
    "anchor_wanted": "best widgets",
    "anchor_found": "best widgets",
    "rel": "",
    "dofollow": true,
    "http_status": 200,
    "frequency": "weekly",
    "last_checked_at": "2026-09-22T10:00:00Z",
    "last_change_at": null,
    "first_seen_at": "2026-08-01T10:00:00Z",
    "checks": 8,
    "hosted_site_id": null
  }
}
```

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

## Stop watching a link

`DELETE /backlinks/links/{link_id}` · scope `backlinks:write`

Stops watching. Its history is kept and comes back if you add the link again.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `link_id` | path | integer | yes | The watched link id. |

### Example

```bash
curl -s -X DELETE "https://app.pbn.ltd/api/v1/backlinks/links/link_id" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.delete("https://app.pbn.ltd/api/v1/backlinks/links/link_id", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/backlinks/links/link_id", {
  method: "DELETE",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "deleted": true,
    "id": 4120
  }
}
```

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

## Check a link now

`POST /backlinks/links/{link_id}/check` · scope `backlinks:write`

Fetches the page immediately and re-reads the link. Costs nothing and uses no allowance; at most once every 10 minutes per link.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `link_id` | path | integer | yes | The watched link id. |

### Example

```bash
curl -s -X POST "https://app.pbn.ltd/api/v1/backlinks/links/link_id/check" \
  -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/backlinks/links/link_id/check", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/backlinks/links/link_id/check", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "id": 4120,
    "source_url": "https://blog.example.com/post/",
    "target_url": "https://mysite.com/page/",
    "status": "live",
    "problem": "",
    "anchor_wanted": "best widgets",
    "anchor_found": "best widgets",
    "rel": "",
    "dofollow": true,
    "http_status": 200,
    "frequency": "weekly",
    "last_checked_at": "2026-09-22T10:00:00Z",
    "last_change_at": null,
    "first_seen_at": "2026-08-01T10:00:00Z",
    "checks": 8,
    "hosted_site_id": null
  }
}
```

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

## List watched backlink profiles

`GET /backlinks/domains` · scope `backlinks:read`



### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `limit` | query | integer | no | Items per page. Default: `50`. |
| `cursor` | query | string | no | next_cursor of the previous page. |

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/backlinks/domains" \
  -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/backlinks/domains", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/backlinks/domains", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": [
    {
      "id": 77,
      "domain": "mysite.com",
      "cadence": "weekly",
      "authority": 249,
      "backlinks": 881,
      "referring_domains": 461,
      "dofollow": 820,
      "nofollow": 61,
      "broken": 635,
      "spam_score": 27,
      "new_referring_domains": 4,
      "lost_referring_domains": 1,
      "last_refresh_at": "2026-09-22T03:00:00Z",
      "refreshes": 12
    }
  ],
  "next_cursor": null,
  "has_more": false
}
```

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

## One backlink profile

`GET /backlinks/domains/{domain_id}` · scope `backlinks:read`

The profile with its top referring domains, anchors, linked pages and monthly history. Reads what is stored - it never spends a refresh.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain_id` | path | integer | yes | The watched domain id. |
| `show` | query | string (one of: live, new, lost) | no | Which referring domains to return. Default: `live`. |

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/backlinks/domains/domain_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/backlinks/domains/domain_id", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/backlinks/domains/domain_id", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "id": 77,
    "domain": "mysite.com",
    "cadence": "weekly",
    "authority": 249,
    "backlinks": 881,
    "referring_domains": [
      {
        "domain": "news.example",
        "authority": 141,
        "backlinks": 4,
        "dofollow": 4,
        "spam_score": 13,
        "state": "live"
      }
    ],
    "dofollow": 820,
    "nofollow": 61,
    "broken": 635,
    "spam_score": 27,
    "new_referring_domains": 4,
    "lost_referring_domains": 1,
    "last_refresh_at": "2026-09-22T03:00:00Z",
    "refreshes": 12,
    "anchors": [
      {
        "anchor": "best widgets",
        "backlinks": 36,
        "referring_domains": 30
      }
    ],
    "top_pages": [
      {
        "url": "https://mysite.com/",
        "backlinks": 400,
        "referring_domains": 210
      }
    ],
    "history": [
      {
        "month": "2026-08",
        "backlinks": 733,
        "referring_domains": 383,
        "new_referring_domains": 57,
        "lost_referring_domains": 6
      }
    ]
  }
}
```

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

## Watch a backlink profile

`POST /backlinks/domains` · scope `backlinks:write`

Refused when it would pass the plan's domain limit, or when the chosen cadence would plan more refreshes a month than the plan includes.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain` | body | string | yes | Any domain. |
| `cadence` | body | string (one of: weekly, monthly, manual) | no | How often the backlink profile is refreshed. A weekly domain uses about 4.3 refreshes a month, monthly 1; "manual" only when you ask. Default: `weekly`. |

### Example

```bash
curl -s -X POST "https://app.pbn.ltd/api/v1/backlinks/domains" \
  -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/backlinks/domains", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/backlinks/domains", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`201`

```json
{
  "data": {
    "id": 77,
    "domain": "mysite.com",
    "cadence": "weekly",
    "authority": 249,
    "backlinks": 881,
    "referring_domains": 461,
    "dofollow": 820,
    "nofollow": 61,
    "broken": 635,
    "spam_score": 27,
    "new_referring_domains": 4,
    "lost_referring_domains": 1,
    "last_refresh_at": "2026-09-22T03:00:00Z",
    "refreshes": 12
  }
}
```

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

## Stop watching a backlink profile

`DELETE /backlinks/domains/{domain_id}` · scope `backlinks:write`



### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain_id` | path | integer | yes | The watched domain id. |

### Example

```bash
curl -s -X DELETE "https://app.pbn.ltd/api/v1/backlinks/domains/domain_id" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.delete("https://app.pbn.ltd/api/v1/backlinks/domains/domain_id", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/backlinks/domains/domain_id", {
  method: "DELETE",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "deleted": true,
    "id": 77
  }
}
```

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

## Refresh a backlink profile now

`POST /backlinks/domains/{domain_id}/refresh` · scope `backlinks:write`

Uses one profile refresh of the monthly allowance, unless a recent shared snapshot of that domain can be reused, in which case it costs nothing. At most once a day per domain.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain_id` | path | integer | yes | The watched domain id. |

### Example

```bash
curl -s -X POST "https://app.pbn.ltd/api/v1/backlinks/domains/domain_id/refresh" \
  -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/backlinks/domains/domain_id/refresh", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/backlinks/domains/domain_id/refresh", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "id": 77,
    "domain": "mysite.com",
    "cadence": "weekly",
    "authority": 249,
    "backlinks": 881,
    "referring_domains": 461,
    "dofollow": 820,
    "nofollow": 61,
    "broken": 635,
    "spam_score": 27,
    "new_referring_domains": 4,
    "lost_referring_domains": 1,
    "last_refresh_at": "2026-09-22T03:00:00Z",
    "refreshes": 12
  }
}
```

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