LocalityBiz API

Introduction

The LocalityBiz API exposes the same business directory that powers the public site. Four endpoints:

The base URL is https://localitybiz.at/api/v1. All responses are JSON, UTF-8. The API never makes write requests; it is read-only.

Authentication

Every request needs an API key. Pass it as a Bearer token in the Authorization header:

curl "https://localitybiz.at/api/v1/search?q=pizza&lat=40.7128&lng=-74.0060&radius_km=2" \
     -H "Authorization: Bearer lk_a3f9b1c7XXXXXXXXXXXXXXXXXXXXXXXX"

Or as a query parameter (handy for quick tests; never use this in production code):

curl "https://localitybiz.at/api/v1/search?api_key=lk_a3f9b1c7XXXXXXXXXXXXXXXXXXXXXXXX&q=pizza&lat=40.7128&lng=-74.0060&radius_km=2"
Generate and manage keys at Dashboard → API → Credentials. Up to 10 keys per account. Each key may be scoped to search, business or all endpoints, and optionally restricted to up to 20 source IPs.
You can view and copy the full key any time from the Credentials page — the value is stored encrypted with the application key, not as a one-time-only secret. Revoke a key (immediate, irreversible) if you suspect it leaked.

Quotas & rate limits

Each subscription has two monthly quotas — one for search, one for business detail. They reset at the start of every Stripe billing period. The current usage is mirrored on every response inside a quota object:

"quota": {
  "limit":     5000,
  "used":      342,
  "remaining": 4658,
  "resets_at": "2026-07-01T00:00:00+00:00",
  "type":      "search"
}

When a quota is exhausted, the API returns HTTP 429 Too Many Requests with a Retry-After header in seconds. There is no per-second rate limit beyond the monthly quota.

Error handling

Every error has a paired HTTP status and a machine-readable JSON error.code:

HTTPerror.codeCause
400bad_requestMissing or invalid parameter. details lists the offending fields.
401unauthorized / invalid_keyNo key, or key not recognised / revoked.
402subscription_inactiveAccount has no active subscription (canceled, past_due, etc.).
403ip_not_whitelisted / scope_deniedIP not in this key's whitelist, or the endpoint type is outside the key's scope.
404not_foundBusiness id unknown.
429quota_exceededMonthly quota for this endpoint type hit. Retry-After header points at the next reset.

Example 429 body:

{
  "success": false,
  "error": {
    "code": "quota_exceeded",
    "message": "Monthly request limit exceeded for your plan.",
    "details": {
      "limit": 5000,
      "used": 5000,
      "remaining": 0,
      "resets_at": "2026-07-01T00:00:00+00:00",
      "type": "search"
    }
  }
}

Response envelope

Every successful response has the shape:

{
  "success": true,
  "data":    /* the payload — object or array */,
  "meta":    /* pagination / query echo (search only) */,
  "quota":   /* usage snapshot — see "Quotas" */
}

Every error response has the shape:

{
  "success": false,
  "error": { "code": "...", "message": "...", "details": { ... } },
  "quota":   /* present only when the error is quota-related */
}

Locations directory

GET /api/v1/locations

Three-level country / region / city tree. Any region or city code returned here can be passed verbatim to Search as the region parameter. Free: requires an API key but never counts against either monthly quota. Cached server-side for 24 hours.

Example request

curl "https://localitybiz.at/api/v1/locations" \
     -H "Authorization: Bearer lk_YOUR_KEY_HERE"

Example response

{
  "success": true,
  "data": [
    {
      "country": "United States",
      "regions": [
        {
          "code": "605",
          "name": "Arizona",
          "cities": [
            { "code": "605_1322", "name": "Buckeye" },
            { "code": "605_1419", "name": "Phoenix" }
          ]
        },
        {
          "code": "612",
          "name": "Colorado",
          "cities": [
            { "code": "612_1707", "name": "Denver" }
          ]
        }
      ]
    },
    {
      "country": "Poland",
      "regions": [
        {
          "code": "812",
          "name": "Mazowieckie",
          "cities": [
            { "code": "812_2031", "name": "Warszawa" }
          ]
        }
      ]
    }
  ]
}
How to use a code:
  • Region-wide search: pass region=605 (whole Arizona).
  • City-narrowed search: pass region=605_1322 (only Buckeye, AZ).
  • Country code is omitted — you cannot search by country directly. Pick a region inside it.

Categories directory

GET /api/v1/categories

Hierarchical schema.org-style taxonomy (up to three levels deep). Each entry carries a code that you pass to Search as category, and a human-readable name. Children appear inside a nested children array; leaf nodes omit that field entirely. Free: requires an API key but never counts against either monthly quota. Cached server-side for 24 hours.

Example request

curl "https://localitybiz.at/api/v1/categories" \
     -H "Authorization: Bearer lk_YOUR_KEY_HERE"

Example response

{
  "success": true,
  "data": [
    {
      "code": "FoodEstablishment",
      "name": "Food & drink",
      "children": [
        {
          "code": "Restaurant",
          "name": "Restaurant",
          "children": [
            { "code": "PizzaShop",        "name": "Pizza shop" },
            { "code": "FastFoodRestaurant", "name": "Fast food restaurant" }
          ]
        },
        { "code": "CafeOrCoffeeShop", "name": "Café & coffee shop" },
        { "code": "BarOrPub",         "name": "Bar & pub" }
      ]
    },
    {
      "code": "MedicalBusiness",
      "name": "Medical services",
      "children": [
        { "code": "Dentist",  "name": "Dentist" },
        { "code": "Physician","name": "Doctor / physician" }
      ]
    }
  ]
}
How to use a code: pass it verbatim as the category filter on /search — e.g. category=Restaurant. Any code at any level works: pick a top-level code for a broad match, a deeper one for a narrow match.

GET /api/v1/search

Full-text business search by EITHER a directory region code OR a (lat, lng, radius_km) circle. Exactly one of the two location modes is required — if region is provided, the geo parameters are ignored. Counts against the search monthly quota.

Up to 30 results per page. When more pages exist the response carries a meta.next_page URL ready to follow; on the last (or only) page that field is omitted. The meta.page_size value reports how many items are actually in data for the current response — full pages, partial last page, or zero on no match.

Parameters

NameTypeDescription
qrequiredstringSearch query. Max 200 characters.
regionstringRegion or region+city code from /locations. "" for the whole region, "_" for one city inside it. When present, lat/lng/radius_km are ignored.
latnumberLatitude of centre, decimal degrees, -90 to 90. Required when region is omitted.
lngnumberLongitude of centre, decimal degrees, -180 to 180. Required when region is omitted.
radius_kmnumberSearch radius in kilometres. Max 50. Required when region is omitted.
pageinteger1-based page. 30 results per page.
categorystringFilter by schema.org type code (e.g. Restaurant, Dentist, NailSalon).
min_ratingnumber0–5. Only businesses rated at or above this value.
has_phonebooleanIf true, drops records that have no phone number.

Example: search inside a region

curl "https://localitybiz.at/api/v1/search?q=pizza&region=605_1322&min_rating=4" \
     -H "Authorization: Bearer lk_YOUR_KEY_HERE"

Example: search inside a circle

curl "https://localitybiz.at/api/v1/search?q=pizza&lat=40.7128&lng=-74.0060&radius_km=2&min_rating=4" \
     -H "Authorization: Bearer lk_YOUR_KEY_HERE"

Example response

{
  "success": true,
  "data": [
    {
      "id": 33597009,
      "name": "Joe's Pizza Greenwich Village",
      "address": "7 Carmine St, New York, NY 10014",
      "lat": 40.7305,
      "lng": -74.0027,
      "distance_km": 2.124,
      "phone": "+1 212-366-1182",
      "rating": 4.6,
      "ratings_count": 18421,
      "main_category": "Restaurant",
      "details_url": "https://localitybiz.at/api/v1/business/33597009"
    }
    /* … up to 30 items */
  ],
  "meta": {
    "page": 1,
    "page_size": 30,                       // count of items actually in `data` (0 .. 30)
    "query": {
      "q": "pizza",
      "lat": 40.7128,
      "lng": -74.006,
      "radius_km": 2,
      "filters": { "category": null, "min_rating": 4, "has_phone": null }
    },
    "next_page": "https://localitybiz.at/api/v1/search?q=pizza&lat=40.7128&lng=-74.006&radius_km=2&min_rating=4&page=2"
  },
  "quota": {
    "limit":     5000,
    "used":      343,
    "remaining": 4657,
    "resets_at": "2026-07-01T00:00:00+00:00",
    "type":      "search"
  }
}

Business detail

GET /api/v1/business/{id}

Full business profile by id (the same id returned in the search response). Counts against the business monthly quota. Returns up to 5 photos and 5 reviews.

Example request

curl "https://localitybiz.at/api/v1/business/33597009" \
     -H "Authorization: Bearer lk_YOUR_KEY_HERE"

Example response

{
  "success": true,
  "data": {
    "id": 33597009,
    "name": "Joe's Pizza Greenwich Village",
    "rating": 4.6,
    "ratings_count": 18421,
    "phones": [
      { "value": "+1 212-366-1182", "primary": true, "note": null }
    ],
    "websites": [
      { "url": "https://joespizzanyc.com", "primary": true, "note": null }
    ],
    "emails": [],
    "address": {
      "full":         "7 Carmine St, New York, NY 10014, USA",
      "street":       "7 Carmine St",
      "locality":     "New York",
      "sub_locality": "Greenwich Village",
      "region":       "New York",
      "postal_code":  "10014",
      "country_code": "US"
    },
    "coords":   { "lat": 40.7305, "lng": -74.0027 },
    "categories": [
      { "code": "Restaurant",  "name": "Restaurant" },
      { "code": "PizzaShop",   "name": "Pizza shop" }
    ],
    "description": "Joe's Pizza has served classic NY slices since 1975 …",
    "photos": [
      { "url": "https://cdn.localitybiz.com/photo1.webp" }
    ],
    "hours": [
      { "day_of_week": 1, "open": "10:00", "close": "22:00", "pause_from": null, "pause_till": null, "notes": null }
    ],
    "services":  [ { "name": "Delivery" }, { "name": "Takeout" } ],
    "amenities": [
      { "code": "accepts_credit_cards", "name": "Accepts credit cards", "category": "payment", "icon": "💳" }
    ],
    "reviews": [
      { "author": "Anna K.", "rating": 5, "text": "Best slice in NYC.", "published_at": "2026-04-12T18:34:00+00:00" }
    ],
    "urls": { "public": "https://localitybiz.com/33597009/joes-pizza-greenwich-village" }
  },
  "quota": {
    "limit":     5000,
    "used":      11,
    "remaining": 4989,
    "resets_at": "2026-07-01T00:00:00+00:00",
    "type":      "business"
  }
}

Pricing & plans

Monthly subscription, billed by Stripe in EUR. Plan changes (upgrades only) and cancellations happen in the Stripe-hosted Customer Portal accessible from your dashboard.

Basic
€29 /month
  • 5,000 search requests/mo
  • 5,000 business detail requests/mo
Pro
€199 /month
  • 50,000 search requests/mo
  • 50,000 business detail requests/mo
Premium
€399 /month
  • 150,000 search requests/mo
  • 150,000 business detail requests/mo
Enterprise
€999 /month
  • 1,000,000 search requests/mo
  • 1,000,000 business detail requests/mo

Subscribe / manage your plan →

OpenAPI spec

The machine-readable specification is at https://localitybiz.at/developers/openapi.json. Drop the URL into Postman, Insomnia, or OpenAPI Generator to scaffold a client library in your language of choice.

Changelog

v1.0.0 — initial public release. Four endpoints: /locations, /categories, /search, /business/{id}. EUR-billed monthly subscriptions via Stripe Checkout + Customer Portal.