Data API
Search Records
Full-text search and filter any Parcel dataset with POST /data/{dataset}/search.
POST https://api.parcelengineering.com/api/v1/data/{dataset}/search{dataset} is one of accounts, contacts, projects, or signals.
The Data API requires the Pro plan. A request from a Free or Starter workspace receives 403 forbidden.
Query parameters
| Parameter | Description |
|---|---|
workspace_id | Attribution. 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.
| Field | Type | Default | Description |
|---|---|---|---|
query | string (min 1) | (none) | Full-text search term. |
filters | object | (none) | Field filters. See Filtering. |
sort.field | string | dataset default | Sort field. Must be a valid sort key for the dataset (see below). Required when sort is present. |
sort.order | asc | desc | asc | Sort direction. |
limit | integer 1..100 | 25 | Records per page. |
offset | integer 0..10000 | 0 | Zero-based record offset for pagination. |
search_after | string | (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
| Dataset | Valid sort fields | Default |
|---|---|---|
accounts | name, created_at | name (asc) |
contacts | name, created_at | name (asc) |
projects | last_signal_at, name, created_at | last_signal_at (desc) |
signals | filed_at, discovered_at | filed_at (desc) |
The Default column applies only when sort is omitted entirely. If you send sort with a field but no order, order defaults to asc, not the dataset default.
Example
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 }'const response = await fetch( 'https://api.parcelengineering.com/api/v1/data/projects/search', { method: 'POST', headers: { 'Authorization': 'Bearer pcl_your_api_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ query: 'South End residential', filters: { stage: { value: 'approved' }, residential_units: { min: 50 }, }, sort: { field: 'last_signal_at', order: 'desc' }, limit: 10, }), });
const { data, metadata } = await response.json();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
| Field | Description |
|---|---|
total | Match count, capped at 1,000. When more than 1,000 records match, total reports 1000 and total_is_capped is true. |
total_is_capped | true when the actual count exceeds the cap and the real number is unknown. |
limit | Page size, echoed back from the request. |
offset | Echoed back from the request, or 0 when paging with search_after (offset is ignored once a cursor is active). |
search_after | Opaque cursor for the next page, or null on the last page. Pass it back as search_after to continue. See Pagination. |
credits | Credits consumed by this request. Equals the number of records returned (limit or fewer). |
Each record returned costs 1 credit toward your workspace’s rolling 24-hour ceiling of 250,000 credits. Use GET /data/usage to check consumption.
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.