Most projects don't need this API. The drop-in widget covers search, filters, and maps with no code. This API is for building your own frontend on Storepoint data.
Read-only API for your published locations. It uses your public locator token and is safe to call from frontend code. To create, update, or delete locations programmatically, use the Location Management API with a private API key.
Overview
The Location Query API reads the location data your team manages in Storepoint. Your team keeps working in the dashboard, spreadsheet imports, Google Sheets sync, or the Location Management API, and you query the same data over REST.
What you can do
- Query locations by geographic radius or retrieve all locations at once
- Filter by tags using the same AND/OR logic configured in your dashboard
- Search by keyword across name, address, description, and tags
- Get your filter configuration to build a matching filter UI
- Access custom field data and priority sorting rules
Common use cases
- Custom locator UI: full design control beyond locator styling
- Mobile apps: native iOS and Android location features
- Additional pages: location detail pages, regional landing pages, "stores near me" sections
- Kiosks and displays: in-store touchscreens, trade show displays
- Integrations: location data for chatbots, voice assistants, or other systems
Quick Start
The Query API is off by default for each locator. Two steps take you from zero to your first response.
1. Enable the API
Open the Developer Portal in your Storepoint dashboard and switch on the Query API. Copy your public token from the same page. You can also restrict requests to your own domains there with the referrer allowlist.
2. Make your first request
const LOCATOR_TOKEN = 'your_locator_token'; // From the Developer Portal
fetch(`https://api.storepoint.co/public/v1/${LOCATOR_TOKEN}/locations?lat=40.7128&lng=-74.0060&radius=25`)
.then(response => response.json())
.then(data => {
data.data.locations.forEach(location => {
console.log(location.name, location.distance.value + ' miles');
});
});
New to Storepoint? Storepoint is a store locator your whole team can run. Start a free trial, add your locations, then query them here.
Authentication
Requests authenticate with your public locator token in the URL path. That is the whole handshake, plain GET requests work as-is.
https://api.storepoint.co/public/v1/{locator_token}/
The token only reads the locations you've published in your locator, the same set your embedded widget shows. That makes it safe to use in frontend code. Enable the API first in the Developer Portal, where you can also restrict requests to your own domains with the referrer allowlist.
Keep your private API key out of frontend code. The private key is for server-side use with the Location Management API. If you expose it by accident, rotate it in your dashboard right away.
Endpoints
Three read-only endpoints cover the full API surface.
| Endpoint | Description |
|---|---|
GET /locations |
Query locations with filtering, sorting, and pagination |
GET /locations/{id} |
Get a single location by its public ID |
GET /filters |
Get filter and tag configuration for building filter UIs |
Query Locations
Retrieve locations with optional geographic filtering, tag filtering, and sorting.
GET /locations
Query parameters
| Parameter | Type | Description |
|---|---|---|
lat |
number | Required*. Latitude of search center point (-90 to 90) |
lng |
number | Required*. Longitude of search center point (-180 to 180) |
radius |
number | Search radius. Default: your locator's configured radius, or 25 if unset |
unit |
string | miles or km. Default: miles |
limit |
number | Maximum results to return. Default: 20, maximum: 500 |
offset |
number | Number of results to skip for pagination. Default: 0 |
tags |
array | Filter by tags. Example: ?tags[]=retail&tags[]=flagship |
tagLogic |
string | any, all, or match. See Filtering by Tags. Default: any |
search |
string | Keyword search across name, address, description, and tags |
sort |
string | distance, name, or priority. See Sorting. Default: distance |
all |
boolean | Set to true to return all locations. Useful for pre-loading or static maps |
* lat and lng are required unless all=true.
Example request
GET /locations?lat=40.7128&lng=-74.0060&radius=25&unit=miles&limit=10
Get Single Location
Retrieve a specific location by its public ID.
GET /locations/{id}
The id is the location's public ID, returned in query results. Format: loc_ followed by alphanumeric characters.
Get Filters
Retrieve your filter and tag configuration to build custom filter UIs. Call this before loading locations to render your dropdowns or checkboxes.
GET /filters
Example response
{
"success": true,
"data": {
"filters": [
{
"id": "_default",
"name": "Categories",
"label": "Filter by Category",
"singleSelect": false,
"inline": false,
"hidden": false,
"hideInLocationDetails": false,
"tags": [
{ "value": "retail", "label": "Retail", "color": "#3B82F6" },
{ "value": "wholesale", "label": "Wholesale" }
]
},
{
"id": "features",
"name": "Features",
"label": "Filter by Features",
"singleSelect": false,
"inline": true,
"hidden": false,
"hideInLocationDetails": false,
"tags": [
{ "value": "parking", "label": "Free Parking", "color": "#10B981" },
{ "value": "accessible", "label": "Wheelchair Accessible" }
]
}
]
}
}
Filter properties
| Property | Description |
|---|---|
id |
Group identifier, used with tagLogic=match for AND across groups |
singleSelect |
If true, only one tag can be selected at a time in this group |
inline |
Suggested display style: inline buttons vs dropdown |
hidden |
If true, this filter is hidden in the standard locator (you may still want to show it) |
tags[].value |
The tag value to use in the ?tags[]= parameter |
tags[].label |
Display label for the tag |
tags[].color |
Optional hex color for styling |
tags[].image |
Optional image URL for the tag |
Response Format
All responses follow one structure.
{
"success": true,
"data": {
"locations": [
{
"id": "loc_abc123def456",
"name": "Downtown Flagship Store",
"coordinates": {
"lat": 40.7128,
"lng": -74.0060
},
"address": {
"formatted": "123 Main Street, New York, NY 10001"
},
"contact": {
"phone": "+1 212-555-1234",
"email": "[email protected]",
"website": "https://example.com"
},
"social": {
"instagram": "storename",
"facebook": "storename",
"twitter": "storename"
},
"hours": {
"monday": "9:00am - 6:00pm",
"tuesday": "9:00am - 6:00pm",
"wednesday": "9:00am - 6:00pm",
"thursday": "9:00am - 8:00pm",
"friday": "9:00am - 8:00pm",
"saturday": "10:00am - 6:00pm",
"sunday": "Closed"
},
"tags": {
"_default": {
"name": "Categories",
"items": [
{ "value": "retail", "label": "Retail", "color": "#3B82F6" },
{ "value": "flagship", "label": "Flagship" }
]
},
"features": {
"name": "Features",
"items": [
{ "value": "parking", "label": "Free Parking", "color": "#10B981" }
]
}
},
"customFields": [
{
"token": "abc123",
"key": "book_appointment",
"name": "Book Appointment",
"type": "button",
"value": "https://booking.example.com",
"label": "Book Now",
"color": "#FF5733"
}
],
"image": "https://cdn.example.com/store.jpg",
"description": "Our flagship downtown location.",
"distance": {
"value": 2.5,
"unit": "miles"
}
}
],
"pagination": {
"offset": 0,
"limit": 20,
"total": 150,
"hasMore": true
}
},
"meta": {
"query": {
"center": { "lat": 40.7128, "lng": -74.0060 },
"radius": 25,
"unit": "miles"
}
}
}
Location object
| Field | Description |
|---|---|
id |
Unique public location ID |
coordinates |
Latitude and longitude |
address |
Address object with a formatted string |
contact |
Phone, email, and website |
social |
Social media handles |
hours |
Operating hours by day, raw text as configured |
tags |
Tags organized by group, each with display metadata |
customFields |
Ordered array of custom fields with full metadata (type, label, color, icon) |
distance |
Distance from the search point, present for radius queries only |
Filtering by Tags
Filter locations with the tags[] parameter and control matching with tagLogic.
| Value | Behavior | Example |
|---|---|---|
any (default) |
Location matches if it has ANY of the specified tags (OR logic) | ?tags[]=retail&tags[]=wholesale returns locations with retail OR wholesale |
all |
Location must have ALL specified tags (AND logic) | ?tags[]=retail&tags[]=parking&tagLogic=all returns locations with both retail AND parking |
match |
AND across filter groups, OR within groups. Matches how multi-select filter UIs typically work | If "retail" and "wholesale" are in Categories and "parking" is in Features, selecting retail + parking returns locations with (retail OR wholesale) AND parking |
Sorting
Control result order with the sort parameter.
distance: closest locations first. The default, and only distance option, for radius queries.name: alphabetical by name. The default, and only name option, forall=truequeries.priority: apply your configured priority sorting rules. Works with both query modes.
Priority sorting. If you've set priority rules in your dashboard (for example, "show flagship stores first within 10 miles"), use sort=priority to apply them. Useful for promoting certain location types in results.
Pagination
Use limit and offset to page through results.
# First page (results 1-20)
GET /locations?lat=40.7&lng=-74.0&limit=20&offset=0
# Second page (results 21-40)
GET /locations?lat=40.7&lng=-74.0&limit=20&offset=20
The response includes pagination metadata with ready-made next and prev URLs.
{
"pagination": {
"offset": 20,
"limit": 20,
"total": 150,
"hasMore": true,
"next": "https://api.storepoint.co/public/v1/abc123/locations?lat=40.7&lng=-74.0&limit=20&offset=40",
"prev": "https://api.storepoint.co/public/v1/abc123/locations?lat=40.7&lng=-74.0&limit=20"
}
}
Pagination fields
| Field | Description |
|---|---|
offset |
Current offset position |
limit |
Number of results per page |
total |
Total number of matching locations |
hasMore |
Whether more results exist beyond this page |
next |
Full URL for the next page, present when hasMore is true |
prev |
Full URL for the previous page, present after the first page |
Error Handling
Errors return one structure with a machine-readable code, a plain message, and a link back to the relevant docs.
{
"success": false,
"error": {
"code": "MISSING_COORDINATES",
"message": "Either provide lat and lng parameters for radius search, or set all=true to retrieve all locations",
"docs": "https://storepoint.co/developers/location-query-api#query-locations"
}
}
Error codes
| Code | Status | Meaning |
|---|---|---|
INVALID_TOKEN |
404 | Locator token not found or inactive |
API_NOT_ENABLED |
403 | The Query API is switched off for this locator. Enable it in the Developer Portal |
REFERRER_NOT_ALLOWED |
403 | The request came from a domain outside your referrer allowlist |
MISSING_COORDINATES |
400 | lat and lng are required unless all=true |
INVALID_COORDINATES |
400 | lat or lng out of valid range |
INVALID_PARAMETER |
400 | Parameter validation failed |
LOCATION_NOT_FOUND |
404 | Location ID not found |
SECRET_KEY_EXPOSED |
400 | Private API key used in a public endpoint. Rotate your key in the dashboard |
Code Examples
JavaScript (Fetch)
const LOCATOR_TOKEN = 'your_locator_token';
const BASE_URL = `https://api.storepoint.co/public/v1/${LOCATOR_TOKEN}`;
// Query locations near a point
async function findNearbyLocations(lat, lng, radius = 25) {
const response = await fetch(
`${BASE_URL}/locations?lat=${lat}&lng=${lng}&radius=${radius}`
);
const data = await response.json();
if (data.success) {
return data.data.locations;
} else {
throw new Error(data.error.message);
}
}
// Get filter configuration for UI
async function getFilters() {
const response = await fetch(`${BASE_URL}/filters`);
const data = await response.json();
return data.data.filters;
}
// Usage
const locations = await findNearbyLocations(40.7128, -74.0060, 25);
locations.forEach(loc => {
console.log(`${loc.name} - ${loc.distance.value} ${loc.distance.unit}`);
});
Python (Requests)
import requests
LOCATOR_TOKEN = 'your_locator_token'
BASE_URL = f'https://api.storepoint.co/public/v1/{LOCATOR_TOKEN}'
def find_nearby_locations(lat, lng, radius=25):
response = requests.get(
f'{BASE_URL}/locations',
params={'lat': lat, 'lng': lng, 'radius': radius}
)
data = response.json()
if data['success']:
return data['data']['locations']
else:
raise Exception(data['error']['message'])
# Usage
locations = find_nearby_locations(40.7128, -74.0060, 25)
for loc in locations:
print(f"{loc['name']} - {loc['distance']['value']} {loc['distance']['unit']}")
cURL
# Query locations
curl "https://api.storepoint.co/public/v1/YOUR_LOCATOR_TOKEN/locations?lat=40.7128&lng=-74.0060&radius=25"
# Get all locations
curl "https://api.storepoint.co/public/v1/YOUR_LOCATOR_TOKEN/locations?all=true"
# Get filters
curl "https://api.storepoint.co/public/v1/YOUR_LOCATOR_TOKEN/filters"
Rate Limits
- Keep sustained traffic under 1,000 requests per minute per token
- Responses are cached at the edge and refreshed automatically when your locations change. Browsers cache responses for 60 seconds.
If a specific use case needs higher limits, email us.
Choose the right approach for your project
Storepoint works at whatever level of code your project calls for. Mix and match freely.
- Zero code. Use the Storepoint locator app out of the box. Manage locations via the dashboard, spreadsheet imports, or Google Sheets sync.
- Customize the locator. Keep our locator UI and extend its behavior with the Widget JavaScript API. Track analytics events, trigger custom actions, integrate with your site.
- Custom frontend, managed backend. Build your own UI with this Query API while your team manages locations in the dashboard.
- Fully API-driven. Sync locations from your systems with the Location Management API, and power your custom frontend with this Query API.
Related resources
- Location Management API: create, update, and delete locations programmatically (server-side)
- Widget JavaScript API: customize and extend the embedded store locator widget
- Storepoint home: the store locator platform and embeddable locator app