Places API

# Layer Selection

How to select Overture, the Open Places API community layer, and account-owned layers in Places API requests.

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. 

**Rule** The stack starts with `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. 

Request URL layers 

```
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. 

1. Create a preset on the dashboard's Layers page, or with `POST /v1/layer-presets`, choosing the layer stack.
2. Make it a search key's default in Layers → Presets and key defaults → Key defaults, or send `PATCH /v1/api-keys/:id/default-layer-preset` with `layerPresetId` set to the preset's ID.
3. Search with that key and omit `layers` and `layer_preset` to 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](/docs/errors#layer%5Fnot%5Favailable) 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)](/agents). 

Request URL preset 

```
GET /v1/places?q=coffee&lat=40.7128&lon=-74.0060&layer_preset=production
```

Sharing

## Share a layer with another account.

Sharing gives another Open Places account read-only access to a layer: one team maintains the entries, and other accounts search them. The receiving account cannot change the layer. Only published entries affect searches. 

Open the Sharing panel on the dashboard's Layers page, or send `POST /v1/layers/:id/grants` to `https://app.openplacesapi.com`with a Control Plane Token that has `grants:write`. You need the other account's ID, shown on their Layers page, and a name for them to use. Send those as `granteeAccountId` and `aliasSlug`: 

JSON bodygrant access

```
{
  "granteeAccountId": "00000000-0000-4000-8000-000000000021",
  "aliasSlug": "partner-places"
}
```

Replace the example account ID with the recipient's ID. The name must start with a lowercase letter or number, use only lowercase letters, numbers, hyphens or underscores, and be at most 63 characters. It cannot collide with one of the recipient's own layer slugs or another grant's name, including archived layers and revoked grants. Only one active grant is allowed per layer and receiving account. 

The receiver adds `account:<name>` to `layers` using their own search API key. For the example name above: 

cURLreceiver's search

```
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:partner-places" \
  -H "Authorization: Bearer $OPA_KEY"
```

They can also use the name in a preset's `selection.accountLayerSlugs` through the management API (for example, `["partner-places"]`, without the `account:` prefix). Shared layers do not yet appear in the receiver's dashboard list or its preset picker. 

Revoke access in the Sharing panel or with `POST /v1/layer-grants/:id/revoke`using `grants:write`. Revoking stops access on the next search request, including searches that use a preset containing the shared layer. 

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 layer stack 

```
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. 

JSON arraytwo entries

```
[
  {
    "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: 

cURLimport entries

```
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](https://openplacesapi.com/docs/layers#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. 

**Limit** Keep active published entries below 5,000 for any one country in a selected layer stack. Approved Open Places community enhancements are loaded separately and currently capped at 1,000 entries for the request country set. 

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. 

**Scopes** `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 list layers 

```
curl https://app.openplacesapi.com/v1/layers \
  -H "Authorization: Bearer $OPA_CONTROL_TOKEN"
```

cURL create layer 

```
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"}'
```
