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/api/v1/ai-visibilityScope 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
curl -s "https://app.pbn.ltd/api/v1/ai-visibility" \
-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/ai-visibility", headers=headers, timeout=120)
print(r.status_code, r.json())
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
{
"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/api/v1/ai-visibility/plansScope 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
curl -s "https://app.pbn.ltd/api/v1/ai-visibility/plans" \
-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/ai-visibility/plans", headers=headers, timeout=120)
print(r.status_code, r.json())
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
{
"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/api/v1/ai-visibility/brandsScope aivis:read
Every brand (domain) you track, with its visibility numbers.
Example
curl -s "https://app.pbn.ltd/api/v1/ai-visibility/brands" \
-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/ai-visibility/brands", headers=headers, timeout=120)
print(r.status_code, r.json())
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
{
"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/api/v1/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
curl -s "https://app.pbn.ltd/api/v1/ai-visibility/brands/brand_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/ai-visibility/brands/brand_id", headers=headers, timeout=120)
print(r.status_code, r.json())
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
{
"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/api/v1/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
curl -s "https://app.pbn.ltd/api/v1/ai-visibility/prompts/prompt_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/ai-visibility/prompts/prompt_id", headers=headers, timeout=120)
print(r.status_code, r.json())
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
{
"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/api/v1/ai-visibility/brandsScope 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
curl -s -X POST "https://app.pbn.ltd/api/v1/ai-visibility/brands" \
-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/ai-visibility/brands", headers=headers, timeout=120)
print(r.status_code, r.json())
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
{
"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/api/v1/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
curl -s -X PATCH "https://app.pbn.ltd/api/v1/ai-visibility/brands/brand_id" \
-H "Authorization: Bearer $PBN_API_KEY"
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())
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
{
"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/api/v1/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
curl -s -X DELETE "https://app.pbn.ltd/api/v1/ai-visibility/brands/brand_id" \
-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/ai-visibility/brands/brand_id", headers=headers, timeout=120)
print(r.status_code, r.json())
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
{
"data": {
"removed": true,
"id": 14
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized, validation_failed
Add prompts
POST/api/v1/ai-visibility/brands/{brand_id}/promptsScope 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
curl -s -X POST "https://app.pbn.ltd/api/v1/ai-visibility/brands/brand_id/prompts" \
-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/ai-visibility/brands/brand_id/prompts", headers=headers, timeout=120)
print(r.status_code, r.json())
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
{
"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/api/v1/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
curl -s -X PATCH "https://app.pbn.ltd/api/v1/ai-visibility/prompts/prompt_id" \
-H "Authorization: Bearer $PBN_API_KEY"
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())
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
{
"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/api/v1/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
curl -s -X DELETE "https://app.pbn.ltd/api/v1/ai-visibility/prompts/prompt_id" \
-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/ai-visibility/prompts/prompt_id", headers=headers, timeout=120)
print(r.status_code, r.json())
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
{
"data": {
"removed": true,
"id": 311
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized, validation_failed
Ask the assistants now
POST/api/v1/ai-visibility/prompts/{prompt_id}/checkScope 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
curl -s -X POST "https://app.pbn.ltd/api/v1/ai-visibility/prompts/prompt_id/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/ai-visibility/prompts/prompt_id/check", headers=headers, timeout=120)
print(r.status_code, r.json())
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
{
"data": {
"queued": true,
"id": 311
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized, unavailable