Local rankings
Where a business sits in Google's map results and local results, town by town.
Local rankings: plan and usage
GET/api/v1/local-rankingsScope local:read
Your plan, what it covers, how much of this month's checks you have used, and how your businesses are doing on the map overall.
Example
curl -s "https://app.pbn.ltd/api/v1/local-rankings" \
-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/local-rankings", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/local-rankings", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"plan": "Local Pro",
"unlimited": false,
"paid_until": "2026-10-31",
"businesses": 3,
"keywords": 96,
"in_map_pack": 31,
"average_map_position": 4.8,
"checks_used": 214,
"checks_included": 700,
"price_from": "9.00"
}
}
Errors: rate_limited, scope_missing, unauthorized
List your businesses
GET/api/v1/local-rankings/businessesScope local:read
Every business you track, with the facts of its Google business profile as we last read them and how it is doing in the map results.
Example
curl -s "https://app.pbn.ltd/api/v1/local-rankings/businesses" \
-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/local-rankings/businesses", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/local-rankings/businesses", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": [
{
"id": 12,
"name": "Top Notch Plumbing",
"domain": "topnotchplumbing.co.uk",
"category": "Plumber",
"address": "150 Bayham St, London NW1 0AU",
"phone": "+442036709508",
"rating": 4.7,
"reviews": 26,
"claimed": true,
"photos": 2,
"keywords": 24,
"in_map_pack": 9,
"average_map_position": 4.2,
"last_checked_at": "2026-09-22T06:12:00Z"
}
]
}
Errors: rate_limited, scope_missing, unauthorized
One business
GET/api/v1/local-rankings/businesses/{business_id}Scope local:read
One business: its profile facts, the towns it is searched from, and every change we have seen to its listing.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
business_id | path | integer | yes | The business id. |
Example
curl -s "https://app.pbn.ltd/api/v1/local-rankings/businesses/business_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/local-rankings/businesses/business_id", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/local-rankings/businesses/business_id", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"id": 12,
"name": "Top Notch Plumbing",
"domain": "topnotchplumbing.co.uk",
"category": "Plumber",
"address": "150 Bayham St, London NW1 0AU",
"phone": "+442036709508",
"rating": 4.7,
"reviews": 26,
"claimed": true,
"photos": 2,
"keywords": 24,
"in_map_pack": 9,
"average_map_position": 4.2,
"last_checked_at": "2026-09-22T06:12:00Z",
"towns": [
{
"id": 4,
"town": "Camden,England,United Kingdom",
"device": "mobile"
}
],
"recent_changes": [
{
"what": "Number of reviews",
"from": "24",
"to": "26",
"at": "2026-09-20T04:10:00Z"
}
]
}
}
Errors: not_found, rate_limited, scope_missing, unauthorized
List your local keywords
GET/api/v1/local-rankings/keywordsScope local:read
Every keyword you track, where it sits in the map results and in the ordinary results, and how it moved since the check before.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
business_id | query | integer | no | Only keywords of this business. |
state | query | string (one of: pack, map, nomap) | no | pack = in the top 3 of the map, map = anywhere in the map results, nomap = not in them at all. |
search | query | string | no | Part of the keyword, business or town. |
limit | query | integer | no | Items per page. Default: 50. |
cursor | query | string | no | next_cursor of the previous page. |
Example
curl -s "https://app.pbn.ltd/api/v1/local-rankings/keywords" \
-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/local-rankings/keywords", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/local-rankings/keywords", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": [
{
"id": 301,
"keyword": "emergency plumber",
"town": "Camden,England,United Kingdom",
"device": "mobile",
"map_position": 2,
"map_change": 1,
"organic_position": 14,
"organic_change": -3,
"best_map_position": 1,
"checked_every": "weekly",
"last_checked_at": "2026-09-22T06:12:00Z"
}
],
"next_cursor": null,
"has_more": false
}
Errors: rate_limited, scope_missing, unauthorized
The history of one keyword
GET/api/v1/local-rankings/keywords/{keyword_id}/historyScope local:read
Every check we have made of that keyword, so you can chart it yourself.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
keyword_id | path | integer | yes | The keyword id. |
limit | query | integer | no | How many checks to return, newest first. Default: 90. |
Example
curl -s "https://app.pbn.ltd/api/v1/local-rankings/keywords/keyword_id/history" \
-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/local-rankings/keywords/keyword_id/history", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/local-rankings/keywords/keyword_id/history", {
method: "GET",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": [
{
"checked_at": "2026-09-22T06:12:00Z",
"map_position": 2,
"organic_position": 14
}
]
}
Errors: not_found, rate_limited, scope_missing, unauthorized
Track more local keywords
POST/api/v1/local-rankings/keywordsScope local:write
Adds keywords to one of your towns. Refused before anything is spent if the month would not fit inside your plan.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
town_id | body | integer | yes | The id of one of your towns (see local.business). |
keywords | body | array | yes | The keywords to track there. |
frequency | body | string (one of: daily, weekly, monthly) | no | How often to check them. Lowered silently if your plan checks less often. Default: weekly. |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/local-rankings/keywords" \
-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/local-rankings/keywords", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/local-rankings/keywords", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
201
{
"data": {
"added": 12,
"town": "Camden,England,United Kingdom"
}
}
Errors: payment_required, rate_limited, scope_missing, unauthorized, validation_failed
Check keywords now
POST/api/v1/local-rankings/checkScope local:write
Puts those keywords at the front of the queue. They are checked within a few minutes and each one uses a check from this month's allowance.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
keyword_ids | body | array | yes | The keyword ids to check (up to 100). |
Example
curl -s -X POST "https://app.pbn.ltd/api/v1/local-rankings/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/local-rankings/check", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/local-rankings/check", {
method: "POST",
headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
Response
200
{
"data": {
"queued": 12
}
}
Errors: payment_required, rate_limited, scope_missing, unauthorized, validation_failed