# AI visibility

Do AI assistants (ChatGPT, Google AI answers and others) name your brand when people ask the questions you care about? Track prompts, answers and competitors.

## AI visibility: plan and usage

`GET /ai-visibility` · scope `aivis:read`

Whether AI visibility is on this account, the plan, how many prompts it covers and how many are tracked, how often they are asked, the number of tracked brands and your average visibility (% of answers that named you). When it is not on the account, plans_url is the page to buy it.

### Example

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

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

### Response

`200`

```json
{
  "data": {
    "active": true,
    "plan": "Growth",
    "paid_until": "2026-10-31",
    "prompts": {
      "used": 12,
      "limit": 25
    },
    "cadence": "weekly",
    "brands": 2,
    "visibility": 38.5,
    "reduced_this_month": false,
    "plans_url": "https://app.pbn.ltd/ai-visibility/plans/"
  }
}
```

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

## AI visibility plans, prices and assistants

`GET /ai-visibility/plans` · scope `aivis:read`

The plans and the extra-prompt pack with their live prices (US dollars a month, before VAT), and the AI assistants a prompt can be asked of (their keys are what `engines` takes). Buying happens on plans_url in the browser - the API never charges.

### Example

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

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

### Response

`200`

```json
{
  "data": {
    "plans": [
      {
        "key": "ai25",
        "name": "Growth",
        "price_usd": "19.00",
        "prompts": 25,
        "cadence": "weekly",
        "summary": ""
      }
    ],
    "pack": {
      "key": "ai10",
      "name": "+10 prompts",
      "price_usd": "5.00",
      "prompts": 10,
      "max_per_account": 40
    },
    "engines": [
      {
        "key": "chatgpt",
        "name": "ChatGPT",
        "default": true
      }
    ],
    "months_offered": [
      1,
      3,
      6,
      12
    ],
    "can_buy": true,
    "plans_url": "https://app.pbn.ltd/ai-visibility/plans/"
  }
}
```

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

## List tracked brands

`GET /ai-visibility/brands` · scope `aivis:read`

Every brand (domain) you track, with its visibility numbers.

### Example

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

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

### Response

`200`

```json
{
  "data": [
    {
      "id": 14,
      "domain": "example.com",
      "brand_name": "Example",
      "hosted_site_id": null,
      "market": {
        "location_code": 2840,
        "location": "United States",
        "country": "US",
        "language_code": "en",
        "language": "English"
      },
      "prompts": 12,
      "visibility": 41.7,
      "prev_visibility": 33.3,
      "share_of_voice": 12.5,
      "mentions": 20,
      "answers": 48,
      "last_checked_at": "2026-09-24T06:00:00Z",
      "url": "https://app.pbn.ltd/ai-visibility/brand/14/"
    }
  ]
}
```

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

## One brand: prompts, latest answers, competitors, trend

`GET /ai-visibility/brands/{brand_id}` · scope `aivis:read`

The brand, every prompt with the latest answer from each assistant, the domains cited instead of you, the daily visibility trend and starter prompts you could add.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `brand_id` | path | integer | yes | The tracked brand id. |
| `days` | query | integer | no | Days of daily visibility trend (1-400). Default: `90`. |

### Example

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

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

### Response

`200`

```json
{
  "data": {
    "id": 14,
    "domain": "example.com",
    "brand_name": "Example",
    "hosted_site_id": null,
    "market": {
      "location_code": 2840,
      "location": "United States",
      "country": "US",
      "language_code": "en",
      "language": "English"
    },
    "prompts": 12,
    "visibility": 41.7,
    "prev_visibility": 33.3,
    "share_of_voice": 12.5,
    "mentions": 20,
    "answers": 48,
    "last_checked_at": "2026-09-24T06:00:00Z",
    "url": "https://app.pbn.ltd/ai-visibility/brand/14/",
    "prompt_list": [
      {
        "id": 311,
        "brand_id": 14,
        "text": "What are the best widget shops?",
        "active": true,
        "engines": [
          "chatgpt",
          "aio"
        ],
        "visibility": 50.0,
        "change": 16.7,
        "checks": 9,
        "answers": 18,
        "mentions": 9,
        "last_checked_at": "2026-09-24T06:00:00Z",
        "latest": [
          {
            "engine": "chatgpt",
            "assistant": "ChatGPT",
            "checked_at": "2026-09-24T06:00:00Z",
            "mentioned": true,
            "cited": true,
            "rank": 2,
            "cited_url": "https://example.com/",
            "snippet": "... Example is a popular choice ...",
            "sources": 6
          }
        ]
      }
    ],
    "competitors": [
      {
        "domain": "rival.com",
        "citations": 14,
        "prompts_hit": 6,
        "last_seen_at": "2026-09-24T06:00:00Z"
      }
    ],
    "trend": [
      {
        "day": "2026-09-24",
        "visibility": 41.7,
        "answers": 12
      }
    ],
    "suggested_prompts": [
      "What are the best Example alternatives?"
    ]
  }
}
```

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

## One prompt: each assistant's answer and the history

`GET /ai-visibility/prompts/{prompt_id}` · scope `aivis:read`

What each assistant answered last time (the full answer text, as on the prompt page) and the last 30 results.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `prompt_id` | path | integer | yes | The prompt id. |

### Example

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

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

### Response

`200`

```json
{
  "data": {
    "id": 311,
    "brand_id": 14,
    "text": "What are the best widget shops?",
    "active": true,
    "engines": [
      "chatgpt",
      "aio"
    ],
    "visibility": 50.0,
    "change": 16.7,
    "checks": 9,
    "answers": [
      {
        "engine": "chatgpt",
        "assistant": "ChatGPT",
        "checked_at": "2026-09-24T06:00:00Z",
        "mentioned": true,
        "cited": true,
        "rank": 2,
        "cited_url": "https://example.com/",
        "snippet": "... Example is a popular choice ...",
        "sources": 6,
        "answer": "Here are some widget shops ..."
      }
    ],
    "mentions": 9,
    "last_checked_at": "2026-09-24T06:00:00Z",
    "history": [
      {
        "engine": "chatgpt",
        "assistant": "ChatGPT",
        "checked_at": "2026-09-24T06:00:00Z",
        "mentioned": true,
        "cited": true,
        "rank": 2,
        "cited_url": "https://example.com/",
        "snippet": "... Example is a popular choice ...",
        "sources": 6
      }
    ]
  }
}
```

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

## Track a brand

`POST /ai-visibility/brands` · scope `aivis:write`

Starts tracking a brand (any domain, hosted with us or not). Then add prompts. Adding one you track already returns it.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `domain` | body | string | yes | The brand's domain, e.g. example.com. |
| `brand_name` | body | string | no | The name people use for it (optional). |
| `location_code` | body | integer | no | Country the answers should be for (codes from GET /rank/locations; default United States). |
| `language_code` | body | string | no | Language of the answers (default English). |

### Example

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

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

### Response

`201`

```json
{
  "data": {
    "id": 14,
    "domain": "example.com",
    "brand_name": "Example",
    "hosted_site_id": null,
    "market": {
      "location_code": 2840,
      "location": "United States",
      "country": "US",
      "language_code": "en",
      "language": "English"
    },
    "prompts": 12,
    "visibility": 41.7,
    "prev_visibility": 33.3,
    "share_of_voice": 12.5,
    "mentions": 20,
    "answers": 48,
    "last_checked_at": "2026-09-24T06:00:00Z",
    "url": "https://app.pbn.ltd/ai-visibility/brand/14/"
  }
}
```

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

## Change a brand's name or market

`PATCH /ai-visibility/brands/{brand_id}` · scope `aivis:write`

The next round of answers uses the new settings.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `brand_id` | path | integer | yes | The tracked brand id. |
| `brand_name` | body | string | no | The name people use for it. |
| `location_code` | body | integer | no | Country of the answers. |
| `language_code` | body | string | no | Language of the answers. |

### Example

```bash
curl -s -X PATCH "https://app.pbn.ltd/api/v1/ai-visibility/brands/brand_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/ai-visibility/brands/brand_id", headers=headers, timeout=120)
print(r.status_code, r.json())
```

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

### Response

`200`

```json
{
  "data": {
    "id": 14,
    "domain": "example.com",
    "brand_name": "Example",
    "hosted_site_id": null,
    "market": {
      "location_code": 2840,
      "location": "United States",
      "country": "US",
      "language_code": "en",
      "language": "English"
    },
    "prompts": 12,
    "visibility": 41.7,
    "prev_visibility": 33.3,
    "share_of_voice": 12.5,
    "mentions": 20,
    "answers": 48,
    "last_checked_at": "2026-09-24T06:00:00Z",
    "url": "https://app.pbn.ltd/ai-visibility/brand/14/"
  }
}
```

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

## Stop tracking a brand

`DELETE /ai-visibility/brands/{brand_id}` · scope `aivis:write` · **destructive**

Stops tracking the brand and all its prompts. The history is kept, but re-adding the brand does not restart its prompts. Needs "confirm": true.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `brand_id` | path | integer | yes | The tracked brand id. |
| `confirm` | body | boolean | yes | Must be true: this cannot be undone from the API. |

### Example

```bash
curl -s -X DELETE "https://app.pbn.ltd/api/v1/ai-visibility/brands/brand_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/ai-visibility/brands/brand_id", headers=headers, timeout=120)
print(r.status_code, r.json())
```

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

### Response

`200`

```json
{
  "data": {
    "removed": true,
    "id": 14
  }
}
```

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

## Add prompts

`POST /ai-visibility/brands/{brand_id}/prompts` · scope `aivis:write`

Adds prompts with the panel's own rule: what fits inside your plan is added, the rest is listed in not_added (add a prompt pack or move up a plan). The first answers usually arrive within the hour.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `brand_id` | path | integer | yes | The tracked brand id. |
| `prompts` | body | array | yes | Whole questions, the way somebody would ask them (or one string, one per line). 6-400 characters each. |
| `engines` | body | array | no | Assistant keys from GET /ai-visibility/plans (engines). Leave out for the default set. Keys that are not on sale are ignored. |

### Example

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

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

### Response

`201`

```json
{
  "data": {
    "added": [
      {
        "id": 311,
        "brand_id": 14,
        "text": "What are the best widget shops?",
        "active": true,
        "engines": [
          "chatgpt",
          "aio"
        ],
        "visibility": 50.0,
        "change": 16.7,
        "checks": 9,
        "answers": 18,
        "mentions": 9,
        "last_checked_at": "2026-09-24T06:00:00Z"
      }
    ],
    "not_added": {
      "count": 0,
      "prompts": [],
      "limit": 25,
      "plans_url": null
    }
  }
}
```

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

## Choose which assistants a prompt is asked of

`PATCH /ai-visibility/prompts/{prompt_id}` · scope `aivis:write`

The next round asks exactly those assistants.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `prompt_id` | path | integer | yes | The prompt id. |
| `engines` | body | array | yes | Assistant keys from GET /ai-visibility/plans. Keys not on sale are ignored; none left means the default set. |

### Example

```bash
curl -s -X PATCH "https://app.pbn.ltd/api/v1/ai-visibility/prompts/prompt_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/ai-visibility/prompts/prompt_id", headers=headers, timeout=120)
print(r.status_code, r.json())
```

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

### Response

`200`

```json
{
  "data": {
    "id": 311,
    "brand_id": 14,
    "text": "What are the best widget shops?",
    "active": true,
    "engines": [
      "chatgpt",
      "aio"
    ],
    "visibility": 50.0,
    "change": 16.7,
    "checks": 9,
    "answers": 18,
    "mentions": 9,
    "last_checked_at": "2026-09-24T06:00:00Z"
  }
}
```

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

## Remove a prompt

`DELETE /ai-visibility/prompts/{prompt_id}` · scope `aivis:write` · **destructive**

Removes the prompt AND its answers history (the same as the Remove button). Needs "confirm": true.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `prompt_id` | path | integer | yes | The prompt id. |
| `confirm` | body | boolean | yes | Must be true: this cannot be undone from the API. |

### Example

```bash
curl -s -X DELETE "https://app.pbn.ltd/api/v1/ai-visibility/prompts/prompt_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/ai-visibility/prompts/prompt_id", headers=headers, timeout=120)
print(r.status_code, r.json())
```

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

### Response

`200`

```json
{
  "data": {
    "removed": true,
    "id": 311
  }
}
```

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

## Ask the assistants now

`POST /ai-visibility/prompts/{prompt_id}/check` · scope `aivis:write`

Asks the prompt's assistants again now (answers usually within the hour). The same bounds as the button: once per prompt in 24 hours and a daily number per account, and not while checks are paused.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `prompt_id` | path | integer | yes | The prompt id. |

### Example

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

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

### Response

`202`

```json
{
  "data": {
    "queued": true,
    "id": 311
  }
}
```

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