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

# Read rows

> Read a layer's or table's rows with conditions, count and total them, look rows up by value, and size a read before you run it, with no map and nothing to download

Most questions are answered by reading rows of Ploti's official records where Ploti keeps them: a page of the parcels over five acres, a count of permits by month, the owner of one parcel number. These calls take a dataset id and answer in the same reply. They need no map and no download. They read [layers and tables](https://ploti.ai/docs/developers/dataset-kinds.md). Documents have [their own calls](https://ploti.ai/docs/developers/documents.md).

## Rows

`datasets rows` returns one page of rows.

* **`columns`**: the columns you want, at most 100. Read real names from `datasets get` first.
* **`where`**: a condition in CQL2-JSON, a standard way to write a filter as JSON. `{"op": ">", "args": [{"property": "acreage"}, 5]}` means acreage over 5. `any_of` takes a list of conditions, any of which a row may match.
* **`place`** or **`bbox`**: a [place](https://ploti.ai/docs/developers/find-datasets.md#places), or a rectangle as `west,south,east,north` in degrees. Tables have no shapes and refuse both.
* **`in_snapshot`** or **`not_in_snapshot`**: used by AI assistants working on a Ploti map, to read only the records they saved there as a snapshot (a saved set of rows on the map). Code that draws its own maps can leave these out.
* **`geometry`**: how much of each shape to return: `none`, `centroid` (a point), `area`, `bbox` or `full`.
* **`order_by`**: up to three columns to sort by.
* **`limit`** and **`cursor`**: 100 rows unless you say. Pass the reply's `nextCursor` to get the next page.

Every reply says how many rows matched and what place the rows came from. A cursor belongs to one request and one version of the dataset. If the dataset gets a new version while you page, the next call answers `stale_cursor`, and you start again.

## Counts and totals

`datasets aggregate` answers with numbers instead of rows: `count`, `sum`, `avg`, `min` and `max` over up to 20 metrics, grouped by up to two columns, with the same conditions as a read. It returns at most 1,000 groups.

## Look up by value

`datasets lookup` finds rows whose columns match the values you give, such as a parcel number, an address or an owner's name, anywhere in the dataset, with no place needed. It takes up to 50 values, matched exactly or by `contains`, on up to 8 columns, and returns up to 50 rows. An exact match that finds nothing is tried again with spaces and dashes stripped, so `12-34-56` finds `123456`.

## Nearby and overlapping

`datasets related` takes one row, such as a parcel, by its dataset and row id. It finds the features of other layers that relate to it: those that intersect, touch, contain, overlap or lie within it, or lie within a distance of up to 5,000 meters. It checks up to 10 layers at a time and returns up to 1,000 features from each.

## Size a read first

`datasets estimate` takes the same request as a read and says how many rows and bytes it would return, and how many pages that is, without returning any. Run it before anything large, then narrow the read with a place, a condition or fewer columns.

## Limits

A page is capped by rows and by size, and the cap depends on the interface:

| Interface | Rows per page | Size per page |
| - | - | - |
| MCP | 1,000 | 200 KB |
| CLI, Python and REST | 5,000 | 20 MB |

* Each read stops after 30 seconds. A sorted read can page up to 50,000 rows in; an unsorted read can page through the whole dataset.
* Each key runs at most 4 reads at once, and reads at most 300,000 rows, 500 MB and 120 seconds of query time a minute. `datasets rows`, `aggregate`, `estimate` and `profile` count toward it.

When you need more than a few pages, [download the dataset](https://ploti.ai/docs/developers/download.md) instead. [Limits and errors](https://ploti.ai/docs/developers/limits-and-errors.md) has the rest.

```bash CLI theme={null}
ploti datasets rows orange-parcels \
  --columns owner,acreage \
  --place "Orange County, FL" \
  --where '{"op": ">", "args": [{"property": "acreage"}, 5]}' \
  --limit 100
```

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

page = ploti.datasets.rows(
    "orange-parcels",
    ["owner", "acreage"],
    place="Orange County, FL",
    where={"op": ">", "args": [{"property": "acreage"}, 5]},
    limit=100,
)
page["matched"], page["nextCursor"]
```

```bash REST theme={null}
curl -X POST https://api.ploti.ai/v1/datasets/orange-parcels/rows \
  -H "Authorization: Bearer $PLOTI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "columns": ["owner", "acreage"],
    "place": "Orange County, FL",
    "where": {"op": ">", "args": [{"property": "acreage"}, 5]},
    "limit": 100
  }'
```

```json MCP theme={null}
{
  "tool": "datasets_rows",
  "arguments": {
    "dataset": "orange-parcels",
    "columns": ["owner", "acreage"],
    "place": "Orange County, FL",
    "where": { "op": ">", "args": [{ "property": "acreage" }, 5] },
    "limit": 100
  }
}
```

```json Reply (trimmed) theme={null}
{
  "datasetId": "orange-parcels",
  "rows": [{ "owner": "SMITH FAMILY TRUST", "acreage": 7.4 }],
  "matched": { "count": 18342, "how": "exact" },
  "scope": "Orange County, FL",
  "nextCursor": "eyJ..."
}
```


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