/

Results appear as you type. Use the up and down arrow keys to move through them and Enter to open one.

The search endpoint

POST /api/v1/search, the request body field by field, what comes back, and why the response is always masked.


A search is a query against the warehouse, so it is a POST with a body rather than a long query string.

The request

POST /api/v1/search
Authorization: Bearer fac_live_…
Content-Type: application/json

{
  "category_ids": [12],
  "country": "NO",
  "admin1_id": 123, "admin2_id": null, "city_id": 456,
  "polygon": null,            // GeoJSON Polygon — excludes the ids above
  "radius": null,             // { lat, lon, metres } — same exclusion
  "filters": {
    "has_website": true,  "has_email": true,
    "has_phone": null,   "phone_type": null,
    "rating_min": 4.0,   "reviews_count_min": "1-50",
    "exclude_permanently_closed": true
  },
  "page": 1, "per_page": 50
}

Field by field

FieldNotes
category_idsArray. Look them up with GET /api/v1/categories?q=…. All 4,000+ are searchable on every plan; how many you may combine in one call follows your tier — 100 on Growth, unlimited on Scale and Enterprise. (Starter has no API; in the app its limit is 25.)
countryISO-3166 alpha-2. Every level below it is optional — omit them all for a country-wide search.
admin1_id, admin2_id, city_idFrom GET /api/v1/geo/divisions and /geo/cities. Labels differ per country; use the level1_label the API returns rather than hardcoding “state”.
polygonGeoJSON Polygon. Mutually exclusive with the admin ids.
radius{ lat, lon, metres }. Also mutually exclusive with the ids.
filtersThe same twelve as the UI. null = do not filter; false = only rows without it.
page, per_pageMax 200 per page.

The response

{
  "total": 638,
  "data": [ { …establishment…,
             "email": "m***@cerv•••••.no",
             "phone": "+47 2* ** *0 90" } ],
  "credits_to_export": 638
}
  • total is the true count over the whole match set, not the page size. Above 100,000 rows it may be an estimate, and then "total_is_estimate": true is present and you should believe it.
  • credits_to_export is what exporting this exact search would cost right now. Use it to gate a job before you create it.
  • Contact fields are always masked, on every plan, for every key. There is no parameter that unmasks them — why. Unmasking happens only through an export, which bills credits.

Searching over the API is free too

Only exports are billed. Poll /search as often as your rate limit allows to size a market, monitor a segment, or drive a UI of your own.

Looking things up first

GET /api/v1/geo/countries
// -> [{ iso2, name, flag, is_live, establishment_count,
//       level1_label, level2_label }]

GET /api/v1/geo/divisions?country=NO&level=1
GET /api/v1/geo/divisions?country=NO&level=2&parent_id=123
GET /api/v1/geo/cities?country=NO&parent_id=456&q=osl
GET /api/v1/categories?q=account

City lookup is a prefix search and returns at most 50, ordered by establishment_count descending.

Turning a search into a file

Pass the same body as the query field of an export: creating and downloading exports.

Updated on:

Was this article helpful?

Related articles