Skip to content

Data API

Search Records

Full-text search and filter any Parcel dataset with POST /data/{dataset}/search.

Endpoint
POST https://api.parcelengineering.com/api/v1/data/{dataset}/search

{dataset} is one of accounts, contacts, projects, or signals.

Query parameters

ParameterDescription
workspace_idAttribution. Required when authenticating with a user JWT (e.g. via the MCP server); optional for API keys. Omitting it on a user-token request returns 400 bad_request.

Request body

All fields are optional. An empty body returns the first 25 records ordered by the dataset’s default sort.

FieldTypeDefaultDescription
querystring (min 1)(none)Full-text search term.
filtersobject(none)Field filters. See Filtering.
sort.fieldstringdataset defaultSort field. Must be a valid sort key for the dataset (see below). Required when sort is present.
sort.orderasc | descascSort direction.
limitinteger 1..10025Records per page.
offsetinteger 0..100000Zero-based record offset for pagination.
search_afterstring(none)Opaque keyset cursor from the previous response’s metadata.search_after. Pass it to fetch the next page. See Pagination.

Sort keys by dataset

DatasetValid sort fieldsDefault
accountsname, created_atname (asc)
contactsname, created_atname (asc)
projectslast_signal_at, name, created_atlast_signal_at (desc)
signalsfiled_at, discovered_atfiled_at (desc)

Example

Search projects
curl -X POST https://api.parcelengineering.com/api/v1/data/projects/search \
-H "Authorization: Bearer pcl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"query": "South End residential",
"filters": {
"stage": { "value": "approved" },
"residential_units": { "min": 50 }
},
"sort": { "field": "last_signal_at", "order": "desc" },
"limit": 10
}'

Response

Response
{
"data": [
{
"id": "8a1f3c2e-0000-0000-0000-000000000001",
"name": "40 Trinity Place",
"primary_address": "40 Trinity Place",
"city": "Boston",
"state": "MA",
"neighborhood": "South End",
"primary_use": "multifamily",
"stage": "approved",
"residential_units": 120,
"last_signal_at": "2025-11-04",
"accounts_count": 3,
"signals_count": 7
}
],
"metadata": {
"total": 42,
"total_is_capped": false,
"limit": 10,
"offset": 0,
"search_after": "eyJ2IjoiMjAyNS0xMS0wNCIsImlkIjoiOGExZjNjMmUtMDAwMy00YjdkLTllNWEtMDAwMDAwMDAwMDAzIiwiZiI6Imxhc3Rfc2lnbmFsX2F0IiwibyI6ImRlc2MiLCJlIjoicHJvamVjdCJ9",
"credits": 10
}
}

The data array is trimmed to one record above; a full limit: 10 page returns 10 records and costs 10 credits (1 per record returned).

Metadata fields

FieldDescription
totalMatch count, capped at 1,000. When more than 1,000 records match, total reports 1000 and total_is_capped is true.
total_is_cappedtrue when the actual count exceeds the cap and the real number is unknown.
limitPage size, echoed back from the request.
offsetEchoed back from the request, or 0 when paging with search_after (offset is ignored once a cursor is active).
search_afterOpaque cursor for the next page, or null on the last page. Pass it back as search_after to continue. See Pagination.
creditsCredits consumed by this request. Equals the number of records returned (limit or fewer).

Pagination

Advance through results with offset. To fetch page 3 with limit: 25, send "offset": 50.

See Pagination for cursor-based paging with search_after.

Filtering

filters accepts field filters, range filters, and relational filters. See Filtering for the full grammar and per-dataset filter keys.

An unknown filter or sort key, or a value outside an enum field’s allowed set (for example an invalid stage), returns 400 rather than an empty result. See Errors for the response shape.