GBPly - Google Business Profile management software

    Using the GBPly Developer API

    Coming soon. The free rank checker is launching soon. Join the waiting list

    Some GBPly features can be driven from your own software over HTTP. Each API is documented below, with the key type it uses, its endpoints and worked examples. Keys are created inside the app, are scoped to a single workspace, and can be revoked at any time.

    How API Access Works

    Every API uses a key that belongs to one workspace. A key can only see that workspace's data, can never create further keys, and can never reach features it wasn't issued for.

    Keys are shown once, at the moment they're created. Store the value somewhere safe — if it's lost, revoke it and create a new one. You can hold up to five active keys per workspace.

    Usage through an API counts against exactly the same allowances and credits as usage inside the app, so there's one bill and one set of limits however you drive it.

    All requests must be made over HTTPS. Never embed a key in a public web page, a mobile app, or anything a customer can view the source of — call the API from a server, a scheduled job or an Apps Script project instead.

    Treat an API key like a password. If you think one has leaked, revoke it in the app first and investigate afterwards — revoking takes effect immediately.

    Free Rank Tracker API — Overview

    The Free Rank Tracker API runs the same local map ranking checks as the Free Rank Check page: a 5×5 grid of 25 points around a centre coordinate, returning the business's Google Maps position at each point.

    The base URL for every call is https://api.gbply.net/v1/rank-tracker, and every request carries your key in an x-api-key header.

    x-api-key: frt_xxxxxxxxxxxxxxxxxxxx

    You must supply the exact Google Place ID for the business. Coordinates are optional — if you leave them out, we look the location up from the Place ID for you. The API never looks a business up by name; a business name can be sent, but it is only used as a display label.

    Free Rank Tracker API — Creating a Check

    Send a POST to /scans with the business's Place ID and the search term. Every create request must also carry an Idempotency-Key header that you generate (8–200 characters).

    POST /scans
    x-api-key: frt_...
    Idempotency-Key: 8f2c1b9a-4d7e-4a11-9c33-6b0e2f5d7a10
    Content-Type: application/json
    
    {
      "placeId": "ChIJ...",
      "keyword": "emergency plumber",
      "gridSpacingKm": 1.5,
      "languageCode": "en",
      "countryCode": "GB"
    }

    That is the smallest useful request. You can still send latitude and longitude to pin the business yourself, and centreLat/centreLng to centre the grid somewhere other than the business. If you send coordinates, they're used exactly as given and no lookup happens; if either is missing, both are resolved from the Place ID.

    {
      "placeId": "ChIJ...",
      "latitude": 53.4084,
      "longitude": -2.9916,
      "centreLat": 53.4084,
      "centreLng": -2.9916,
      "keyword": "emergency plumber",
      "gridSpacingKm": 1.5,
      "zoomLevel": 14
    }

    gridSpacingKm accepts 0.2–10, zoomLevel accepts 3–21, and the language and country codes are two letters each. Only the Place ID and search term are required.

    If the Place ID can't be found you'll get invalid_place_id, and if the lookup itself fails you'll get place_lookup_failed. Neither uses up a free check or a credit.

    A successful call returns 202 Accepted with the new check and a hint for how long to wait before polling.

    {
      "scan": { "scan_id": "...", "status": "running", "keyword": "emergency plumber", "points": null, "summary": null },
      "poll_after_seconds": 5
    }

    Free Rank Tracker API — Retries and Idempotency

    The Idempotency-Key makes retries safe. If a request times out or the connection drops, send the identical request with the same key and you'll get the original check back — never a second one, and never a second charge against your allowance.

    Use a fresh key for every genuinely new check. Reusing a key from an earlier check returns that earlier check instead of starting a new one.

    If two copies of the same request land at the same instant, one gets duplicate_request; read the check back with a GET and carry on.

    A random UUID per check is the simplest approach. If you're driving this from a spreadsheet, the row ID plus the search term plus the date works just as well.

    Free Rank Tracker API — Polling and Results

    Read a check by its ID, or list your recent checks.

    GET /scans/{scan_id}
    GET /scans?limit=20

    While the status is queued or running, the response includes poll_after_seconds and a Retry-After header. Don't poll faster than that. Checks normally finish within one to two minutes.

    {
      "scan": {
        "scan_id": "...",
        "status": "complete",
        "place_id": "ChIJ...",
        "keyword": "emergency plumber",
        "grid_size": 5,
        "grid_spacing_km": 1.5,
        "points": [{ "row": 0, "col": 0, "lat": 53.4, "lng": -2.9, "rank": 4, "status": "found" }],
        "summary": {
          "points_total": 25,
          "points_found": 19,
          "points_failed": 0,
          "avg_rank_where_found": 6.2,
          "pct_top_3": 24.0,
          "pct_top_10": 68.0
        },
        "created_at": "...",
        "completed_at": "..."
      }
    }

    A check's status is queued, running, complete, partial (some points failed) or failed. Each point's status is found, not_found or failed.

    Responses contain ranking data only — no internal costs, account identifiers or provider details.

    Free Rank Tracker API — Remaining Allowance

    Check how much of the monthly free allowance is left before you start a batch.

    GET /usage
    
    { "scans_limit": 10, "scans_used": 3, "scans_remaining": 7, "period_month": "2026-09" }

    The allowance is per account per calendar month and is shared with in-app usage. Once it's spent, checks draw on the workspace's paid Rank Tracker credits; with no credits left you'll get no_credits.

    Free Rank Tracker API — Limits

    • One active check per workspace at a time.

    • 10 create requests per minute and 300 per day, per key.

    • 120 read requests per minute, per key.

    • The grid is fixed at 5×5 (25 points); spacing 0.2–10 km.

    If you exceed a limit you'll get rate_limited with a Retry-After header — wait that long and try again rather than retrying immediately.

    Free Rank Tracker API — Errors

    Every error response has the same shape: a human-readable message plus a stable machine code you can branch on. Match on the code, never on the message text.

    { "error": "Monthly free allowance used up", "code": "quota_exhausted" }

    • invalid_api_key (401) — missing, unknown or revoked key.

    • forbidden (403) — not permitted for this caller.

    • validation_error (400) — a field failed validation.

    • scan_not_found (404) — unknown check, or not yours.

    • scan_in_progress (409) — one active check per workspace.

    • duplicate_request (409) — concurrent retry; read the check back.

    • quota_exhausted (429) — monthly free allowance spent.

    • no_credits (402) — allowance spent and no credits left.

    • rate_limited (429) — slow down; honour Retry-After.

    • api_disabled (503) — API temporarily switched off.

    • invalid_place_id (400) — the Place ID could not be found.

    • place_lookup_failed (502) — the location lookup for that Place ID failed.

    • provider_unavailable (502) — upstream ranking data unavailable.

    • internal_error (500) — unexpected failure.

    Free Rank Tracker API — Example: curl

    Create a check, then poll it until it finishes.

    BASE_URL="https://api.gbply.net/v1/rank-tracker"
    
    # create
    curl -s -X POST "$BASE_URL/scans" \
      -H "x-api-key: $FRT_KEY" \
      -H "Idempotency-Key: $(uuidgen)" \
      -H "Content-Type: application/json" \
      -d '{
        "placeId": "ChIJ...",
        "keyword": "emergency plumber",
        "gridSpacingKm": 1.5
      }'
    
    # poll
    curl -s "$BASE_URL/scans/SCAN_ID" -H "x-api-key: $FRT_KEY"
    
    # remaining allowance
    curl -s "$BASE_URL/usage" -H "x-api-key: $FRT_KEY"

    Free Rank Tracker API — Example: Google Sheets

    This Apps Script function runs a check for one row of a spreadsheet and writes the average position back. Store the key in Script Properties rather than in the sheet itself.

    const BASE_URL = 'https://api.gbply.net/v1/rank-tracker';
    
    function runCheck(placeId, keyword) {
      const key = PropertiesService.getScriptProperties().getProperty('FRT_KEY');
      const headers = { 'x-api-key': key };
    
      const create = UrlFetchApp.fetch(BASE_URL + '/scans', {
        method: 'post',
        contentType: 'application/json',
        headers: Object.assign({ 'Idempotency-Key': Utilities.getUuid() }, headers),
        payload: JSON.stringify({ placeId: placeId, keyword: keyword, gridSpacingKm: 1.5 }),
        muteHttpExceptions: true
      });
      const created = JSON.parse(create.getContentText());
      if (create.getResponseCode() >= 400) throw new Error(created.code);
    
      const id = created.scan.scan_id;
      for (var i = 0; i < 24; i++) {
        Utilities.sleep(5000);
        const read = UrlFetchApp.fetch(BASE_URL + '/scans/' + id, { headers: headers, muteHttpExceptions: true });
        const scan = JSON.parse(read.getContentText()).scan;
        if (scan.status !== 'queued' && scan.status !== 'running') return scan;
      }
      throw new Error('timed_out');
    }

    Because Apps Script triggers can re-run, always generate the idempotency key once per row and reuse it on a retry — that way a re-run can never spend a second check.

    Sheets formulas time out quickly. For more than a handful of rows, run the checks from a time-based trigger and write results back as they arrive rather than from a custom function.