How to Build a Store-Locator List of the Nearest Places

API or service
Places API, Route Matrix API
Task
Location → nearest places sorted by distance → list, map, travel times
Examples
URLAI
Difficulty
Beginner
Time
10 min

You have a location, such as a map click, the user's position, or a geocoded address, and need a store-locator list: the nearest supermarkets, pharmacies, or stores of one brand, numbered and sorted by distance, with markers on a map. The Geoapify Places API returns the places around the location with their distance, nearest first.

Send a category, a circle filter for the search area, and a proximity bias. Show the results in the order they come back, and load the next page with offset when the user wants more. Optionally, replace the straight-line distances with walking or driving times from the Route Matrix API.

Task flow: location → Places API → nearest places with distance → numbered list and markers → optional Route Matrix travel times.

Plan the store list

Decide these values before you send the request:

Choice Parameter Notes
What to find categories, optionally name Use a category key from the Places category list, such as commercial.supermarket or healthcare.pharmacy. To list the stores of one brand, add name; see How to Search Places by Name or Brand.
Where to search filter=circle:longitude,latitude,radiusMeters The radius is in metres. Places outside the circle are not returned.
Sort order bias=proximity:longitude,latitude Orders the results by distance from the point and adds properties.distance in metres. Use the same point as the circle centre.
List length limit Places per page. The default is 20, the maximum is 500. A short list of 5–10 places is easier to scan.
More results offset Zero-based index of the first place on the page. Keep every other parameter unchanged and increase offset by limit.

Coordinates in circle and proximity are ordered as longitude, latitude.

For each row in the list, the response gives you:

  • properties.name: the store name. Some places have no name; show a fallback such as the category.
  • properties.housenumber and properties.street, or properties.address_line2: a short address line. properties.formatted holds the full address.
  • properties.distance: the straight-line distance in metres from the proximity point.
  • geometry.coordinates: the marker position as [longitude, latitude].
  • properties.place_id: keep it to request Place Details such as opening hours when the user opens a store.

Number the list rows and the map markers the same way, so users can match them. Fit the map to the search circle or to the returned places.

Straight-line distance is quick to show but doesn't follow streets. When the walking or driving time matters, add one Route Matrix request for the list; see Add walking or driving times.

Request the nearest stores

This request returns the 5 nearest supermarkets within 2,000 metres of Pike Place Market in Seattle:

https://api.geoapify.com/v2/places?categories=commercial.supermarket&filter=circle:-122.3421,47.6101,2000&bias=proximity:-122.3421,47.6101&limit=5&offset=0&apiKey=YOUR_API_KEY

The response lists the places nearest first. Shown as a store list:

# Name Address Distance
1 H Mart 1601 2nd Avenue 141 m
2 Dan's Belltown Grocery 2221 3rd Avenue 487 m
3 PCC Corner Market 1320 4th Avenue 526 m
4 City Foods 2522 5th Avenue 917 m
5 Whole Foods Market 2200 Westlake Avenue 960 m

Reduced response: The response below keeps the first two places and the fields the list and the map use.

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "name": "H Mart",
        "housenumber": "1601",
        "street": "2nd Avenue",
        "city": "Seattle",
        "state": "Washington",
        "postcode": "98101",
        "formatted": "H Mart, 1601 2nd Avenue, Seattle, WA 98101, United States of America",
        "categories": ["commercial", "commercial.supermarket"],
        "distance": 141,
        "place_id": "512f04ef9ac6955ec059eaf8799d1ece4740f00103f901f2e432940100000092030648204d617274"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [-122.3402469, 47.6103093]
      }
    },
    {
      "type": "Feature",
      "properties": {
        "name": "Dan's Belltown Grocery",
        "housenumber": "2221",
        "street": "3rd Avenue",
        "city": "Seattle",
        "state": "Washington",
        "postcode": "98121",
        "formatted": "Dan's Belltown Grocery, 2221 3rd Avenue, Seattle, WA 98121, United States of America",
        "categories": ["building", "building.commercial", "commercial", "commercial.supermarket", "wheelchair", "wheelchair.yes"],
        "distance": 487,
        "place_id": "510d07614d0c965ec059d7ffc3189dce4740f00102f901011cc1130000000092031644616e27732042656c6c746f776e2047726f63657279"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [-122.34450087046953, 47.614169212057895]
      }
    }
  ]
}

A place can have extra categories, such as building or wheelchair.yes, next to the one you searched for.

Load the next page

For a "Show more" button, repeat the request with offset=5. All other parameters stay the same:

https://api.geoapify.com/v2/places?categories=commercial.supermarket&filter=circle:-122.3421,47.6101,2000&bias=proximity:-122.3421,47.6101&limit=5&offset=5&apiKey=YOUR_API_KEY

This page continues the list with places 6 to 10, from Saveway Market at 1,187 m to QFC at 1,668 m. Continue the numbering from the first page. When a page returns fewer places than limit, there are no more places in the circle. For deduplication and other paging details, see How to Paginate Through Place Search Results.

An empty features array on the first page means there are no matching places in the circle. Offer a larger radius or another category.

Add walking or driving times with the Route Matrix API

properties.distance from the Places API is a straight-line distance. In a city, the walk or drive along the streets can be noticeably longer, and it can change which store is actually closer. The Route Matrix API calculates the travel time and road distance from the search point to every store on the page in one request.

Send the search point as the only source and the stores as targets, in the same order as the list:

POST https://api.geoapify.com/v1/routematrix?apiKey=YOUR_API_KEY
Content-Type: application/json
{
  "mode": "walk",
  "sources": [
    { "location": [-122.3421, 47.6101] }
  ],
  "targets": [
    { "location": [-122.3402469, 47.6103093] },
    { "location": [-122.34450087046953, 47.614169212057895] },
    { "location": [-122.3352648, 47.6090875] },
    { "location": [-122.345931, 47.6179284] },
    { "location": [-122.3381763, 47.6183162] }
  ]
}

Matrix locations are [longitude, latitude], the same order as geometry.coordinates in the Places response, so you can copy them directly. Use "mode": "walk" for a city-centre store list and "mode": "drive" when users come by car. Other modes are listed in the Routing API Travel Modes table.

Reduced response: With one source and five targets, sources_to_targets has one row with five cells. time is in seconds and distance in metres.

{
  "sources_to_targets": [
    [
      { "distance": 191, "time": 182, "source_index": 0, "target_index": 0 },
      { "distance": 629, "time": 569, "source_index": 0, "target_index": 1 },
      { "distance": 706, "time": 649, "source_index": 0, "target_index": 2 },
      { "distance": 1210, "time": 1093, "source_index": 0, "target_index": 3 },
      { "distance": 1109, "time": 983, "source_index": 0, "target_index": 4 }
    ]
  ],
  "units": "metric",
  "distance_units": "meters",
  "mode": "walk"
}

Match each cell to its store with target_index, then show the time and road distance in place of the straight-line distance. Minutes are time / 60, rounded:

# Name Straight line (Places API) On foot (Route Matrix API)
1 H Mart 141 m 3 min · 191 m
2 Dan's Belltown Grocery 487 m 9 min · 629 m
3 PCC Corner Market 526 m 11 min · 706 m
4 City Foods 917 m 18 min · 1,210 m
5 Whole Foods Market 960 m 16 min · 1,109 m

City Foods is nearer in a straight line, but Whole Foods Market is the shorter walk. If the list should be ordered by travel time, sort the rows by time after the matrix response arrives, and keep the original Places order as a fallback. Treat a cell without a finite time as unreachable and keep its straight-line distance.

Cost. Route Matrix credits are max(sources, targets) × min(sources, targets, 10). With one source, that is one credit per store: this request with 5 stores costs 5 credits, a page of 10 stores costs 10. Because the list already works without it, make the travel times optional, for example a "Show walking time" checkbox, or request them only for the page the user is looking at. See Route Matrix pricing.

For the full nearest-by-travel-time selection flow, see How to Find the Nearest Driver, Store, or Facility by Travel Time.

Store locator with an AI coding agent

Copy this prompt into Codex, Claude Code, Cursor, or another coding agent to build a store-locator list with a map in your project.

Build a store locator in my project with the Geoapify Places API: a numbered list of the nearest places, sorted by distance, with matching markers on a map.

Before you start
- Inspect the existing project first. Reuse its current framework, components, styling, HTTP client, map library, and conventions. Make the smallest necessary changes.
- Ask only what the project doesn't answer: which places to list (a category such as commercial.supermarket, optionally a brand name), where the search point comes from (map click, user location, or an address), the radius, the page size, and whether to add optional walking or driving times.
- Ask me to configure a Geoapify API key before live requests. On a backend, read it from GEOAPIFY_API_KEY. In a browser, ask whether to use a restricted key or a backend proxy. Never hardcode, print, or log the key.

API flow
1. Send GET https://api.geoapify.com/v2/places with categories, filter=circle:lon,lat,radiusMeters, bias=proximity:lon,lat (same point), limit, and offset. Add name only for a brand search; name matches place names, and categories is still required.
2. Show the features in the returned order. For each row use properties.name (with a fallback when it is missing), a short address from housenumber and street or address_line2, and properties.distance in metres. Put a numbered marker at geometry.coordinates ([longitude, latitude]) with the same number as the row.
3. For "Show more", repeat the request with offset increased by limit and continue the numbering. Fewer results than limit means there are no more places in the circle.
4. Optional travel times: when the user turns them on, POST https://api.geoapify.com/v1/routematrix with mode walk or drive, the search point as the only source, and the places of the current page as targets in list order. Match sources_to_targets[0] cells to rows by target_index and show time (seconds, as minutes) and road distance instead of the straight-line distance. Keep the straight-line distance when a cell has no finite time. With one source, the request costs one credit per place, so keep it off by default.

Requirements
- Keep longitude before latitude in circle, proximity, and matrix locations.
- Treat an empty features array as "no places in this area" and suggest a larger radius.
- Ignore responses from older requests when the user moves the point or changes the search quickly.
- Handle HTTP 400 messages, 401, timeouts, and limited 429 retries with backoff. Show loading states.
- Keep the map attribution, including "Powered by Geoapify", visible.

Review the generated code against the requests in this guide. Check that list numbers match marker numbers, offset paging continues the numbering, the travel-time request is optional and maps cells by target_index, and the API key isn't exposed.