PBN.LTD API docs
View as Markdown

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-rankings

Scope 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/businesses

Scope 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

NameInTypeRequiredDescription
business_idpathintegeryesThe 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/keywords

Scope 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

NameInTypeRequiredDescription
business_idqueryintegernoOnly keywords of this business.
statequerystring (one of: pack, map, nomap)nopack = in the top 3 of the map, map = anywhere in the map results, nomap = not in them at all.
searchquerystringnoPart of the keyword, business or town.
limitqueryintegernoItems per page. Default: 50.
cursorquerystringnonext_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}/history

Scope local:read

Every check we have made of that keyword, so you can chart it yourself.

Parameters

NameInTypeRequiredDescription
keyword_idpathintegeryesThe keyword id.
limitqueryintegernoHow 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/keywords

Scope local:write

Adds keywords to one of your towns. Refused before anything is spent if the month would not fit inside your plan.

Parameters

NameInTypeRequiredDescription
town_idbodyintegeryesThe id of one of your towns (see local.business).
keywordsbodyarrayyesThe keywords to track there.
frequencybodystring (one of: daily, weekly, monthly)noHow 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/check

Scope 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

NameInTypeRequiredDescription
keyword_idsbodyarrayyesThe 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