> ## Documentation Index
> Fetch the complete documentation index at: https://ploti.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Keys and setup

> Make an API key, choose what it may do, and set up the CLI, the Python library, plain REST calls or an MCP connection

Everything you build on Ploti's official real estate records signs in with an **API key**: a secret string that lets a program act as you in your workspace, your team's Ploti account. Make one in Ploti, pick what it may do, and give it to whichever of the four interfaces you use. All four reach the same data and the same operations.

The short version, with the CLI:

```bash theme={null}
npm install -g @ploti/cli
ploti login
ploti places list
```

## Make an API key

A workspace owner or admin makes keys in Ploti under **Settings**, then **API keys**. A key starts with `pmcp_`, and Ploti shows it once. Keep it where you keep other secrets, such as an environment variable named `PLOTI_API_KEY`, and never paste it into a chat with an assistant.

A key acts as the workspace member who made it: their coverage areas (the counties the workspace pays for), their credits and their access. It stops working when that member leaves the workspace. Make the key for a long-running integration from an account that will stay, such as a shared service member.

## Coverage, plan and credits

Three things about your workspace decide what any key can reach:

* **Coverage areas** are the US counties your workspace pays for. A county or city dataset opens when its county is in your coverage, a statewide dataset when your coverage has a county in its state, and a national dataset with any coverage at all. A dataset outside them comes back `outside_coverage`, or marked locked in a search. An admin adds counties in Ploti; [Coverage areas](https://ploti.ai/docs/getting-started/coverage-areas.md) explains how and what they cost.
* **Your plan** is the workspace's paid Ploti subscription. Every call needs an active plan (otherwise `no_subscription`), and a dataset the plan doesn't include comes back `locked`. It is separate from a Data Feeds subscription.
* **Credits** pay for collecting data Ploti doesn't hold yet, through [research requests](https://ploti.ai/docs/research/overview.md). Reading and downloading what Ploti already holds spends none. The balance is in Ploti's settings.

## What a key may do

Each key holds only the permissions it was given. A call that needs a permission the key lacks is refused with `403` `insufficient_scope`, and the message says which one to add.

| Permission | Id | Lets the key |
| - | - | - |
| Read | `read` | Search datasets, read rows and documents, download datasets and their stored files, look up municipal codes, list changes, and read research requests. |
| File requests | `requests` | File research requests, which can spend credits, answer their questions, and file the missing data they find. |
| Webhooks | `webhooks` | Add, test and remove webhooks (addresses of yours that Ploti calls when something happens), and watch datasets for changes. |
| Maps | `maps` | Make maps and save results on them, as AI assistants do to show what they found ([Show results on a map](https://ploti.ai/docs/mcp/maps.md)). |
| Data Feeds | `feeds` | Read and manage Data Feeds subscriptions, versions and files. |

A new key gets every permission, Data Feeds included. So does a sign-in through `ploti login`, and so does an AI assistant that signs in itself, though assistants are offered no Data Feeds tools. Any member can read and download what the workspace's subscriptions deliver. Creating, changing and deleting a subscription takes a workspace admin.

A key for a dashboard can read without being able to spend credits. A key for a service that watches permits needs `read` and `webhooks`.

## Set up an interface

> [tab] CLI
>
> The CLI is the `ploti` command. It needs Node.js 20 or later, and 22.15 or later to trim, merge or convert a [download](https://ploti.ai/docs/developers/download.md).
>
> ```bash theme={null}
> npm install -g @ploti/cli
> # or run any command without installing
> npx @ploti/cli places list
> ```
>
> On your own machine, `ploti login` prints a code and opens Ploti in your browser. Approve the code and pick a workspace, and the CLI keeps the login. `ploti logout` signs out. The login is stored:
>
> * on macOS, in the Keychain
> * on Linux, in the Secret Service, or in `~/.config/ploti/credentials.json`, readable only by you, when none is running
> * on Windows, in `.config\ploti\credentials.json` in your user profile
>
> On a server, in a cron job or in CI, set `PLOTI_API_KEY` instead. `PLOTI_API_URL` points the CLI at another API address.
>
> Commands follow one pattern, `ploti <resource> <verb>`, such as `ploti datasets rows`. `ploti commands` lists them all, and `ploti help datasets rows` shows one command's fields.
>
> * Fields are flags named after the API's fields, in kebab-case (`--section-id`). Required ones can also be given in order.
> * A list takes commas, and an object takes JSON.
> * `--json '{...}'` passes a whole request, and `--raw` prints the JSON reply.
>
> To teach a coding agent the commands, `ploti skills install <folder>` writes Ploti's skills (short instruction files agents read) into the folder your agent reads them from.

> [tab] Python
>
> The `ploti` Python package calls every operation as a function and returns plain dicts.
>
> It is not on PyPI yet. It comes installed and signed in where Ploti's own AI agent runs code inside the Ploti app. To use it on your own machine today, [ask us](mailto:support@ploti.ai) for the package, then install it from its folder with pip:
>
> ```bash theme={null}
> pip install ./sdk-python              # no dependencies
> pip install './sdk-python[parquet]'   # also reads downloaded Parquet files
> ```
>
> Set `PLOTI_API_KEY` to your key. `PLOTI_API_URL` points it at another API address.
>
> Calls follow the same tree as the CLI: `ploti datasets rows` is `ploti.datasets.rows()`. Arguments are snake\_case (`section_id=`), and replies are the API's JSON as plain dicts, with camelCase keys (`page["nextCursor"]`). A map's own calls are on the map object that `ploti.maps.create()` returns, such as `m.snapshots.create()`.

> [tab] REST
>
> Every operation is an HTTPS endpoint under `https://api.ploti.ai/v1`. Send the key as a bearer token:
>
> ```bash theme={null}
> curl https://api.ploti.ai/v1/places \
>   -H "Authorization: Bearer $PLOTI_API_KEY"
> ```
>
> * Paths follow the same tree: reading rows is `POST /v1/datasets/{dataset}/rows`, where `{dataset}` is the dataset's id.
> * Fields are camelCase over REST (`mapId`), where Python and MCP write them in snake\_case (`map_id`). REST accepts either spelling.
> * `POST` and `PATCH` take a JSON body of at most 256 KB; `GET` and `DELETE` take query parameters.
> * A list takes `limit` and `cursor` and returns `nextCursor`, which is `null` on the last page.
> * Creates answer `201` and take an `Idempotency-Key` header: a retry with the same key within a day returns the first answer instead of creating twice.
>
> [`https://api.ploti.ai/v1/openapi.json`](https://api.ploti.ai/v1/openapi.json) describes every operation, its input and its output, and [`/v1/llms.txt`](https://api.ploti.ai/v1/llms.txt) is the same API as one plain-text guide.

> [tab] MCP
>
> MCP (Model Context Protocol) is the standard way AI assistants plug in outside tools. Connected over MCP, an assistant calls Ploti's operations as tools, named by the same tree with underscores: reading rows is `datasets_rows`.
>
> * **An assistant you use**, such as Claude, ChatGPT, Claude Code or Cursor, connects to `https://mcp.ploti.ai/api/mcp` and signs in through Ploti in your browser. [AI agents](https://ploti.ai/docs/mcp/overview.md) has the steps for each one.
> * **An agent you run yourself** connects to `https://api.ploti.ai/api/mcp` with an API key as a bearer token.
>
> The MCP examples on these pages show the tool and the arguments an assistant sends. You normally ask the assistant in plain words and it picks the tools. Data Feeds is not offered over MCP; use one of the other three for it.

## A first call

The example on the right lists the places your workspace covers, each with how many datasets it holds:

* each county
* each state that has a covered county
* each state's covered counties as one place (`fl-counties`)
* `us`, for national records

This workspace covers only Orange County, so Florida's counties hold what Orange County holds. `--state FL` keeps Florida's places, which leaves out `us`. Every other page uses these place ids, or plain names such as `"Orange County, FL"`, to narrow what it reads.

```bash CLI theme={null}
ploti places list --state FL
```

```python Python theme={null}
import ploti

ploti.places.list(state="FL")
```

```bash REST theme={null}
curl "https://api.ploti.ai/v1/places?state=FL" \
  -H "Authorization: Bearer $PLOTI_API_KEY"
```

```json MCP theme={null}
{
  "tool": "places_list",
  "arguments": { "state": "FL" }
}
```

```json Reply (trimmed) theme={null}
{
  "places": [
    {
      "id": "fl-county-orange",
      "name": "Orange County",
      "state": "FL",
      "kind": "county",
      "datasetCount": 214,
      "datasetCounts": { "parcels-property": 9, "zoning-land-use": 62 }
    },
    {
      "id": "fl",
      "name": "Florida",
      "state": "FL",
      "kind": "state",
      "datasetCount": 31,
      "datasetCounts": { "government-records": 12 }
    },
    {
      "id": "fl-counties",
      "name": "Every Florida county",
      "state": "FL",
      "kind": "state_counties",
      "datasetCount": 214,
      "datasetCounts": { "parcels-property": 9, "zoning-land-use": 62 }
    }
  ],
  "nextCursor": null
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.