Search

Maps, Places & Reviews

Local business data from Google Maps: search the map, list the businesses that match a query in a named place, read one place's reviews, and get one place's full details: its attributes (payments, service options, children…) and menu.

Endpoints#

  • GET/search/maps
  • GET/search/places
  • GET/search/reviews
  • GET/search/place-details

Credits: 1 per request. These four endpoints also count against a tighter local rate-limit group, on top of the search group: a request must pass both (see Rate Limiting).

Maps Parameters#

ParameterTypeRequiredDescription
qstringYesSearch query (1–500 chars). A place name in the query geocodes on its own, e.g. coffee shops in austin
limitnumberNoResults (1–20, default: 20)
pagenumberNoPage number (1–100, default: 1)
glstringNoCountry code, max 5 chars (e.g. us). With no ll, lat or lng it also sets where the search looks (see below)
hlstringNoLanguage code, max 5 chars (e.g. en)
llstringNoViewport as a Google ll token, max 64 chars, e.g. @30.2672,-97.7431,13z
latnumberNoLatitude to steer results toward (-90 to 90)
lngnumberNoLongitude to steer results toward (-180 to 180)
zoomnumberNoMap zoom (1–21)
cachebooleanNofalse skips the cached copy and looks the places up now. Same credits (default: true)

Maps Response#

JSON
{  "searchParameters": { "q": "coffee shops in austin", "type": "maps", "gl": "us", "hl": "en", "num": 20, "page": 1, "ll": "@39.5,-98.35,5z" },  "lite": false,  "page": 1,  "totalResults": 20,  "places": [    {      "position": 1,      "title": "Example Coffee Roasters",      "placeId": "ChIJ7cv00Dm1RIYRx5mHcIYlGmE",      "cid": "0x8644b5a4e3d8f52b:0x5e1c2c3a4b5d6e7f",      "featureId": "/g/11c5k8tq1n",      "category": "Coffee shop",      "categories": ["Coffee shop", "Cafe"],      "rating": 4.7,      "reviews": 1834,      "price": "$$",      "address": "123 Congress Ave, Austin, TX 78701",      "latitude": 30.2672,      "longitude": -97.7431,      "website": "https://coffee.example.com/",      "domain": "coffee.example.com",      "phone": "(512) 555-0100",      "timezone": "America/Chicago",      "description": "Small-batch roaster with a patio.",      "openState": "Open · Closes 6 PM",      "openNow": true,      "businessStatus": "OPERATIONAL",      "hours": null,      "openingDate": null,      "openingDateText": null,      "attributes": {        "service_options": { "serves_dine_in": true, "has_takeout": true, "has_delivery": false },        "payments": { "requires_cash_only": false, "pay_credit_card": true, "pay_mobile_nfc": true }      },      "thumbnailUrl": "https://lh5.googleusercontent.com/...",      "reviewsLink": "https://www.google.com/maps/...",      "bookingLink": null,      "bookingLinks": null    }  ],  "source": "searlo.tech(engine)",  "credits": 1}
  • lite: true means Google served its reduced record. Since October 2026 that is what Google Maps shows every signed-out visitor, so expect lite: true on maps and places: no price level, and accessibility as the only attribute group (the sample above shows the full record). A lite answer is never cached.
  • businessStatus says whether the place still trades: OPERATIONAL, CLOSED_TEMPORARILY or CLOSED_PERMANENTLY, as the Places API names them, or null when Google's record does not say. It is never guessed from missing data.
  • openNow is true or false for whether the place is open right now, or null when the record does not say. openState is Google's own state line, in the language of hl ("Abierto · Cierre: 21:30" with hl=es). A place that has closed (businessStatus CLOSED_TEMPORARILY or CLOSED_PERMANENTLY) has both set to null.
  • With no ll, lat or lng, gl also sets where the search looks: the main city of that country, for 51 countries (gl=es with farmacia answers from Spain, not the US). A place named in q (hoteles en tokio) decides for itself. gl=us, no gl, and a country without a default area keep the US-wide default. searchParameters.ll echoes the area used.
  • In a maps result, cid is the 0x…:0x… place id, featureId is a Knowledge Graph id (/g/…) and placeId is the Google Place ID (ChIJ…).
  • bookingLink is the single link to send a user to when the place takes bookings or orders; bookingLinks holds the full set.
  • attributes is Google's "About" list for the place, grouped as Google groups it (service_options, payments, children, parking, offerings…) and keyed by Google's own ids, the same in every language. A yes/no attribute is true or false (attributes.payments.requires_cash_only is true on a cash-only place, false where Google says it is not), a value attribute is a string (price_level), a list one an array (pay_credit_card_types_accepted). It is null when Google lists none, and a lite answer carries accessibility only.
  • A place listed before it opens has openingDateText, Google's text in your hl ("Opens 1 Nov"), and openingDate, the date read from it ("2026-11-01"). Both are null on a place that is open, and so is openState on one that is not open yet. /search/places carries the same two fields.
  • A maps query that finds nothing answers 200 with places: [], and is not charged.

Places Parameters#

GET /search/places lists the businesses that match a query in a place you name, instead of a map viewport.

ParameterTypeRequiredDescription
qstringYesWhat to look for (1–500 chars), e.g. dentist
locationstringNoWhere to search, max 200 chars, e.g. Austin, Texas
limitnumberNoResults (1–20, default: 20)
pagenumberNoPage number (1–100, default: 1)
glstringNoCountry code, max 5 chars
hlstringNoLanguage code, max 5 chars
llstringNoViewport token, as on maps
latnumberNoLatitude (-90 to 90)
lngnumberNoLongitude (-180 to 180)
cachebooleanNofalse skips the cached copy and looks the places up now. Same credits (default: true)

Places Response#

JSON
{  "searchParameters": { "q": "dentist", "type": "places", "location": "Austin, Texas", "gl": "us", "hl": "en", "num": 20, "page": 1 },  "lite": false,  "page": 1,  "totalResults": 20,  "places": [    {      "position": 1,      "title": "Example Family Dental",      "address": "456 Lamar Blvd, Austin, TX 78703",      "latitude": 30.2849,      "longitude": -97.7545,      "rating": 4.9,      "ratingCount": 412,      "category": "Dentist",      "categories": ["Dentist", "Cosmetic dentist"],      "priceLevel": null,      "phoneNumber": "(512) 555-0199",      "website": "https://dental.example.com/",      "domain": "dental.example.com",      "openState": "Closed · Opens 8 AM",      "openNow": false,      "businessStatus": "OPERATIONAL",      "hours": null,      "openingDate": null,      "openingDateText": null,      "attributes": { "payments": { "pay_credit_card": true, "requires_cash_only": false } },      "thumbnailUrl": "https://lh5.googleusercontent.com/...",      "cid": "0x8644b5a4e3d8f52b:0x2f1e3d4c5b6a7980",      "featureId": "0x8644b5a4e3d8f52b:0x2f1e3d4c5b6a7980",      "knowledgeGraphId": "/g/1tf6yx2q",      "placeId": "ChIJN1t_tDeuEmsRUsoyG83frY4"    }  ],  "source": "searlo.tech(google_places)",  "credits": 1}

A places query that finds nothing returns 200 with places: [], and is not charged. openState, openNow, businessStatus and lite mean the same as on maps.

Reviews Parameters#

GET /search/reviews returns the reviews for one place. Identify it one of four ways. placeId, featureId and cid are faster than q, which costs a place lookup first.

ParameterTypeRequiredDescription
placeIdstringOne of fourA Google Place ID (ChIJ…), as on a maps result or from the Places API
featureIdstringOne of fourThe 0x…:0x… place id: featureId on a places result, or cid on a maps result
cidstringOne of fourThe decimal place id from a Google Maps share link (digits only, max 32). Not the 0x… value
qstringOne of fourA place name to look up (1–500 chars); the top match is used
limitnumberNoReviews to return (1–50, default: 10). A call returns the first page Google serves, about eight reviews: limit can only lower that
glstringNoCountry code, max 5 chars
hlstringNoLanguage code, max 5 chars. Sets the language of relativeDate

Reviews Response#

JSON
{  "searchParameters": { "type": "reviews", "featureId": "0x8644b5a4e3d8f52b:0x2f1e3d4c5b6a7980", "hl": "en", "gl": "us", "num": 10 },  "place": {    "title": "Example Family Dental",    "address": "456 Lamar Blvd, Austin, TX 78703",    "featureId": "0x8644b5a4e3d8f52b:0x2f1e3d4c5b6a7980",    "rating": 4.9,    "reviewCount": 412  },  "totalResults": 8,  "hasMore": true,  "hasNextPage": true,  "nextPageToken": "CAESBkVnSUlDQQ",  "reviews": [    {      "position": 1,      "reviewId": "ChZDSUhNMG9nS0VJQ0FnSUNf",      "rating": 5,      "text": "Friendly staff and no wait.",      "date": "2026-09-14T16:20:00.000Z",      "timestamp": 1789402800,      "relativeDate": "2 weeks ago",      "language": "en",      "author": "Jordan P.",      "authorProfile": "https://www.google.com/maps/contrib/...",      "authorPhoto": "https://lh3.googleusercontent.com/...",      "authorId": "104512345678901234567",      "authorReviewCount": 37,      "isLocalGuide": true,      "response": { "text": "Thank you, Jordan!", "relativeTime": "a week ago", "date": "2026-09-21T09:00:00.000Z", "language": "en" }    }  ],  "source": "searlo.tech(google_reviews)",  "credits": 1}
  • Each call returns the first page of reviews Google serves with the place, about eight, in any language: hl sets the language of relativeDate, and language is the one each review was written in.
  • hasMore (also hasNextPage) is true when the place has more reviews than that page. Later pages cannot be fetched yet: nextPageToken, when present, is Google's token for them, and no parameter takes it. place.reviewCount is the lifetime count.
  • date is the ISO time, timestamp the same in Unix seconds and relativeDate Google's own text.
  • response is present only when the owner replied: text, relativeTime, date and language.
  • A place with no reviews answers 200 with reviews: [], and is not charged. A place Google reports reviews for but sent without them answers 503 REVIEWS_WITHHELD (retryable: true), also not charged: retry the request.

Place Details Parameters#

GET /search/place-details returns everything Google's place panel holds for one place: every field a maps result has, plus the full attribute list and the menu. Identify the place as on reviews; a placeId costs no lookup.

ParameterTypeRequiredDescription
placeIdstringOne of fourA Google Place ID (ChIJ…)
featureIdstringOne of fourThe 0x…:0x… place id
cidstringOne of fourThe decimal place id from a Google Maps share link
qstringOne of fourA place name to look up (1–500 chars); the top match is used
glstringNoCountry code, max 5 chars
hlstringNoLanguage code, max 5 chars; sets the language of labels, descriptions and the menu
menubooleanNofalse skips the menu lookup for a place that serves food (a faster answer when you do not need the menu)

Place Details Response#

JSON
{  "searchParameters": { "type": "place-details", "placeId": "ChIJV8rJWPR1nkcRfAVGOrZIeVs", "featureId": "0x479e75f458c9ca57:0x5b7948b63a46057c", "hl": "en", "gl": "de" },  "lite": true,  "place": {    "title": "Augustiner Klosterwirt",    "placeId": "ChIJV8rJWPR1nkcRfAVGOrZIeVs",    "cid": "0x479e75f458c9ca57:0x5b7948b63a46057c",    "featureId": "/g/1ptx9q8dt",    "category": "Bavarian restaurant",    "rating": 4.5,    "reviews": 14740,    "address": "Augustinerstraße 1, 80331 München-Altstadt-Lehel, Germany",    "website": "http://augustiner-klosterwirt.de/",    "phone": "+498955054466",    "openState": "Open · Closes 12 AM",    "openNow": true,    "businessStatus": "OPERATIONAL",    "hours": [{ "day": "Friday", "hours": ["9:30 AM–12 AM"] }],    "attributes": {      "accessibility": { "has_wheelchair_accessible_entrance": true, "has_wheelchair_accessible_restroom": true }    },    "menu": {      "url": "https://augustiner-klosterwirt.de/index.php/speisen-getraenke/speisekarte",      "sections": [        {          "name": "Soups",          "items": [{ "name": "Bavarian Liver Dumpling Soup", "description": "With fresh parsley", "price": "€7.50" }]        }      ],      "photos": []    },    "menuStatus": "found"  },  "source": "searlo.tech(google_place)",  "credits": 1}
  • place carries every field of a maps result (openState, openNow, businessStatus, openingDate, bookingLinks…) with the same meaning.
  • menu is the menu Google holds for the place: url (the restaurant's own menu page) and sections of items with descriptions and prices as Google shows them, in the language hl names. Since October 2026 Google Maps shows signed-out visitors no menu, so for a place that serves food the menu is read from the place's Google Search panel instead. photos is empty for now. Any part Google does not have is empty, and menu is null when it has none.
  • menuStatus says how the menu lookup went: found; none (Google holds no menu for the place); pending (still being read: ask again in a few minutes and it is there, which is how a large batch gets its menus, on a second pass); unavailable (the lookup failed: retry later). It is absent for a place that serves no food. A menu is kept for 7 days, so asking again costs no wait.
  • lite: true means Google answered its reduced place record: since October 2026 that is what Google Maps shows every signed-out visitor, with accessibility as the only attribute group and no price level. A lite answer is never cached.
  • A placeId that names no business (an address or plus-code id) or a q that matches nothing answers 400, an id Google does not know 404, both with errorCode: "PLACE_NOT_FOUND" and not charged.

Examples

# Map searchcurl "https://api.searlo.tech/api/v1/search/maps?q=coffee+shops+in+austin&limit=10" \  -H "x-api-key: sk_your_api_key" # Looked up now instead of from the cache; gl=es places the search in Spaincurl "https://api.searlo.tech/api/v1/search/maps?q=farmacia&gl=es&hl=es&cache=false" \  -H "x-api-key: sk_your_api_key" # Businesses in a named placecurl "https://api.searlo.tech/api/v1/search/places?q=dentist&location=Austin,+Texas" \  -H "x-api-key: sk_your_api_key" # Everything about one place, by its Google Place IDcurl "https://api.searlo.tech/api/v1/search/place-details?placeId=ChIJd2ts_zTbmUcRqrBZ97_BxpA&hl=en" \  -H "x-api-key: sk_your_api_key" # Reviews for one placecurl "https://api.searlo.tech/api/v1/search/reviews?featureId=0x8644b5a4e3d8f52b:0x2f1e3d4c5b6a7980&limit=20" \  -H "x-api-key: sk_your_api_key"
const API = "https://api.searlo.tech/api/v1";const headers = { "x-api-key": process.env.SEARLO_API_KEY }; // 1. Find the businessesconst placesParams = new URLSearchParams({ q: "dentist", location: "Austin, Texas" });const places = await (await fetch(`${API}/search/places?${placesParams}`, { headers })).json(); // 2. Read the reviews of the first one that carries a featureIdconst place = places.places.find(p => p.featureId);if (!place) throw new Error("No result carried a featureId");const reviewParams = new URLSearchParams({ featureId: place.featureId, limit: "20" });const reviews = await (await fetch(`${API}/search/reviews?${reviewParams}`, { headers })).json(); console.log(reviews.place.title, reviews.place.rating, reviews.place.reviewCount);reviews.reviews.forEach(r => console.log(r.rating, r.relativeDate, r.text.slice(0, 80)));
import osimport requests res = requests.get(    "https://api.searlo.tech/api/v1/search/maps",    params={"q": "coffee shops in austin", "limit": 10, "gl": "us"},    headers={"x-api-key": os.environ["SEARLO_API_KEY"]},)data = res.json()if data.get("lite"):    print("Reduced tier served: review counts, price and hours may be missing")for p in data["places"]:    if p["businessStatus"] in ("CLOSED_TEMPORARILY", "CLOSED_PERMANENTLY"):        continue  # the place has closed    print(p["position"], p["title"], p["rating"], p["reviews"], p["address"], p["openNow"])

Stuck? Write to [email protected], or check live status.