How to Paginate Through Place Search Results
- API or service
- Places API
- Task
- Focused place query → consecutive result pages
- Examples
- Difficulty
- Beginner
- Time
- 10 min
You have a focused Places API query that returns more matches than fit in one response and need to request subsequent pages. Use limit to set the page size and offset to identify where the next page starts.
Keep the categories, conditions, and spatial filter unchanged while paging. Pagination is suitable for loading more results from a defined search; it does not turn a broad regional query into a guaranteed complete place-data export.
Understand limit and offset
limit controls the maximum number of places in one response. Its default is 20 and its maximum is 500. offset is zero-based and defaults to 0.
This request returns the first 100 supermarkets from the specified Vienna rectangle:
https://api.geoapify.com/v2/places?categories=commercial.supermarket&filter=rect:16.1790307,48.0623698,16.5567679,48.2849935&limit=100&offset=0&apiKey=YOUR_API_KEY
Reduced response: A real page contains up to the requested limit. This response keeps its first place and the fields needed for display and deduplication.
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "Eurospar",
"housenumber": "184",
"street": "Brünner Straße",
"city": "Vienna",
"postcode": "1210",
"country": "Austria",
"country_code": "at",
"formatted": "Eurospar, Brünner Straße 184, 1210 Vienna, Austria",
"categories": [
"commercial",
"commercial.supermarket"
],
"lat": 48.2830702,
"lon": 16.4139929,
"place_id": "5136c02700fc69304059bba800b43b244840f00102f901824d730d000000009203084575726f73706172"
},
"geometry": {
"type": "Point",
"coordinates": [16.4140015, 48.283072]
}
}
]
}
The next request keeps the search unchanged and skips the first 100 results:
https://api.geoapify.com/v2/places?categories=commercial.supermarket&filter=rect:16.1790307,48.0623698,16.5567679,48.2849935&limit=100&offset=100&apiKey=YOUR_API_KEY
Increase offset by limit for every page: 0, 100, 200, and so on. Stop when the response contains fewer features than the requested limit. An empty features array means there are no results at that offset.
Paginate place results with JavaScript
This example loads consecutive pages for one fixed category and rectangle. It deduplicates features by place_id and stops when a short page indicates that no full page remains.
async function getPlacePages({
apiKey,
categories,
filter,
pageSize = 100,
maxPages = 10
}) {
const places = [];
const seenPlaceIds = new Set();
for (let page = 0; page < maxPages; page += 1) {
const params = new URLSearchParams({
categories,
filter,
limit: String(pageSize),
offset: String(page * pageSize),
apiKey
});
const response = await fetch(
`https://api.geoapify.com/v2/places?${params}`
);
if (!response.ok) {
throw new Error(`Places request failed: ${response.status}`);
}
const pageData = await response.json();
// Keep only one feature for each stable Geoapify place identifier.
for (const feature of pageData.features) {
const placeId = feature.properties.place_id;
if (!placeId || seenPlaceIds.has(placeId)) continue;
seenPlaceIds.add(placeId);
places.push(feature);
}
// A short page is the final page for this query.
if (pageData.features.length < pageSize) break;
}
return { type: "FeatureCollection", features: places };
}
const places = await getPlacePages({
apiKey: "YOUR_API_KEY",
categories: "commercial.supermarket",
filter: "rect:16.1790307,48.0623698,16.5567679,48.2849935"
});
console.log(places);
Keep pageSize between 1 and 500. maxPages protects the application from an unexpectedly large loop; decide whether reaching it should return a partial result, ask the user to narrow the search, or continue after confirmation.
Paginate place results with Python
This version performs the same focused search with the third-party requests package. It keeps the query constant, increases the offset, and preserves one feature per place_id.
import requests
PLACES_URL = "https://api.geoapify.com/v2/places"
def get_place_pages(
api_key,
categories,
place_filter,
page_size=100,
max_pages=10,
):
places_by_id = {}
with requests.Session() as session:
for page in range(max_pages):
response = session.get(
PLACES_URL,
params={
"categories": categories,
"filter": place_filter,
"limit": page_size,
"offset": page * page_size,
"apiKey": api_key,
},
timeout=30,
)
response.raise_for_status()
page_features = response.json()["features"]
# A dictionary removes duplicates while retaining insertion order.
for feature in page_features:
place_id = feature["properties"].get("place_id")
if place_id:
places_by_id.setdefault(place_id, feature)
if len(page_features) < page_size:
break
return {
"type": "FeatureCollection",
"features": list(places_by_id.values()),
}
places = get_place_pages(
api_key="YOUR_API_KEY",
categories="commercial.supermarket",
place_filter="rect:16.1790307,48.0623698,16.5567679,48.2849935",
)
print(f"Retrieved {len(places['features'])} places")
Handle 429 responses and add retry delays when processing many pages. If the function reaches max_pages, record that the result may be partial or ask the caller to continue explicitly.