Places API
Layer Selection
On this page
How layers work
Layer order is precedence order.
The default stack uses the Overture-backed base layer and the Open Places API community layer. Authenticated callers can also select account-owned layers or a saved preset. Higher layers win when they explicitly target a lower-layer place.
Higher layers win
account:<layer_slug> Your account's layers — creates, patches, suppressions, private entries. wins open Approved Open Places community enhancements. base The Overture-backed base release. base, then optional open, then account layers — so
layers=base,open,account:field-team reads bottom-up.
Explicit selection
Use layers for a specific ordered stack.
Use layers when a request needs deterministic layer membership. Account layers use the
account:<layer_slug> token and come last.
GET /v1/places?q=coffee&lat=40.7128&lon=-74.0060&layers=base,open,account:field-team Presets
Save presets and set key defaults.
Presets are useful for production defaults, customer-specific views, or partner-managed entries. A request can
use layers or layer_preset, but not both.
- Create a preset on the dashboard's Layers page, or with
POST /v1/layer-presets, choosing the layer stack. - Make it a search key's default in Layers → Presets and key defaults → Key defaults, or send
PATCH /v1/api-keys/:id/default-layer-presetwithlayerPresetIdset to the preset's ID. - Search with that key and omit
layersandlayer_presetto use its default.
An explicit layers or layer_preset in the request overrides the key default.
If the default preset is archived, searches using it fail with layer_not_available instead of falling back.
Search keys also authenticate MCP. The MCP search_places tool has no layers or layer_preset input,
so an agent uses its key's default preset. See Connect an agent (MCP).
GET /v1/places?q=coffee&lat=40.7128&lon=-74.0060&layer_preset=production Operations
Account layers can express product-specific changes.
| Operation | Effect in search |
|---|---|
| Create place | Adds an account-only place with an account-scoped public ID. |
| Update place field | Patches selected fields on a targeted lower-layer place while preserving the public ID. |
| Suppress place | Removes a targeted lower-layer place from the effective result set. |
| Mark closed, moved, or duplicate | Represents product-specific status without mutating the shared base layer. |
curl -G "https://api.openplacesapi.com/v1/places" \
--data-urlencode "q=coffee" \
--data-urlencode "lat=40.7128" \
--data-urlencode "lon=-74.0060" \
--data-urlencode "layers=base,open,account:client-a" \
-H "Authorization: Bearer $OPA_KEY" Imports
Import entries in bulk.
Import between 1 and 500 entries at a time. The dashboard's Import JSON form takes an array of entry
objects like this one. This example adds Bryant Park to your layer and hides a coffee result from
your searches; replace the target ID with the place_id you want to suppress.
[
{
"clientEntryKey": "bryant-park",
"operation": "create_place",
"fieldsPatch": { "name": "Bryant Park" },
"country": "US",
"latitude": 40.7536,
"longitude": -73.9832
},
{
"clientEntryKey": "hide-coffee-result",
"operation": "suppress_place",
"targetPublicPlaceId": "overture:a27acb33-8aaf-4cf8-a979-4dc1cfe5dc82",
"fieldsPatch": {},
"country": "US"
}
]
Every entry needs operation and fieldsPatch; use an empty object for a suppression.
A create_place entry cannot have a targetPublicPlaceId. Other operations require
that field with an overture:, open: or acct: public ID.
For a new place, include a name and coordinates so it can be found in searches.
To automate imports, send POST /v1/layers/:id/imports to https://app.openplacesapi.com
with a Control Plane Token that has imports:write. Wrap the array in an object with
entries and optional publish. Save that object as import.json, then run:
curl "https://app.openplacesapi.com/v1/layers/$LAYER_ID/imports" \
-H "Authorization: Bearer $OPA_CONTROL_TOKEN" \
-H "Content-Type: application/json" \
--data @import.json
Set publish: true to publish every imported entry immediately. If publish is false
or omitted, all imported entries become drafts. This flag overrides each entry's state,
including when updating entries on re-import. The dashboard's “Publish imported entries immediately”
checkbox controls the same flag.
An optional clientEntryKey (1–160 characters) identifies an entry within this layer.
Re-importing the same key replaces that entry's supplied data instead of adding another entry;
an archived entry is restored. Keep keys unique within each import. Without a key, every import
creates a new entry. Imports also count toward your plan's layer-entry allowance and the
published-entry runtime limits.
Runtime limits
Layer reads are live today and capped per country set.
Public searches live-read published account-layer entries from the control plane. For each layer-enabled search, the runtime loads up to 5,000 published account-layer entries matching the selected account-layer stack and request country set. Entries without a country count only when they target a current candidate place or their coordinates fall within the request's bounding box. If no country set is available, the runtime uses those target and coordinate checks for all entries.
The dashboard warns when a layer approaches the 5,000-entry runtime cap. Archive stale entries, split very large imports across layers or presets, or keep high-volume compiled layer artifacts on your roadmap for workloads that need more than the live-read path.
Management API
Use Control Plane Tokens for layer writes.
Public search API keys authenticate /v1/places and MCP searches. External systems that create layers, write
entries, run imports, save presets, manage grants, or update API-key layer defaults should use scoped Control
Plane Tokens from the dashboard.
layers:read, layers:write, entries:write, imports:write,
presets:write, grants:write, and api_keys:write_defaults.
| Endpoint | Scope |
|---|---|
GET /v1/layers | layers:read |
POST /v1/layers, PATCH /v1/layers/:id, POST /v1/layers/:id/archive | layers:write |
POST /v1/layers/:id/entries, PATCH /v1/layers/:id/entries/:entryId | entries:write |
POST /v1/layers/:id/imports | imports:write |
POST /v1/layer-presets, PATCH /v1/layer-presets/:id | presets:write |
POST /v1/layers/:id/grants, POST /v1/layer-grants/:id/revoke | grants:write |
PATCH /v1/api-keys/:id/default-layer-preset | api_keys:write_defaults |
curl https://app.openplacesapi.com/v1/layers \
-H "Authorization: Bearer $OPA_CONTROL_TOKEN" curl https://app.openplacesapi.com/v1/layers \
-H "Authorization: Bearer $OPA_CONTROL_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Field team","slug":"field-team"}'