Querying
How to filter, sort, select, and paginate with the Micro API
All data in Micro is queried through a single endpoint:
POST /v2/prism/{teamId}/{objectType}/queryThe request body contains a query object that describes what to fetch.
Select
Section titled “Select”select is required. Pass an array of property slugs to return.
{ "query": { "select": ["full_name", "email", "title"] }}Use dot notation to traverse relationships:
{ "query": { "select": ["full_name", "company.name", "company.primary_domain"] }}Filter
Section titled “Filter”filter is an array of conditions. Each condition is { slug: { operator: value } }.
{ "query": { "select": ["full_name", "email"], "filter": [ { "labels": { "in": ["investor"] } }, { "last_interaction_date": { ">=": "2024-01-01" } } ] }}Operators
Section titled “Operators”| Operator | Types | Description |
|---|---|---|
= | all | Exact match |
!= | all | Not equal |
< > <= >= | number, date | Comparison |
in | string, select | Value is in the array |
not_in | string, select | Value is not in the array |
begins_with | string | Starts with value |
ends_with | string | Ends with value |
contains | string | Contains value |
not_contains | string | Does not contain value |
between | number, date | Within a two-element [min, max] array, inclusive on both ends |
exists | any | Field has a value |
not_exists | any | Field has no value |
is_null | any | Alias for not_exists — takes any truthy value |
is_not_null | any | Alias for exists — takes any truthy value |
Each filter object takes exactly one property with exactly one operator.
Combining filters
Section titled “Combining filters”By default filters are combined with AND. Use combinator to switch to OR:
{ "query": { "select": ["full_name"], "filter": [ { "labels": { "in": ["investor"] } }, { "labels": { "in": ["founder"] } } ], "combinator": "OR" }}sort is an array of { slug: direction } objects. Earlier items in the array take priority.
{ "query": { "select": ["full_name", "last_interaction_date"], "sort": [ { "last_interaction_date": "desc" }, { "full_name": "asc" } ] }}Pagination
Section titled “Pagination”Use limit to set page size. It must be between 1 and 50 — requests above the cap are rejected rather than clamped.
{ "query": { "select": ["full_name"], "limit": 50 }}The response wraps rows in a data array alongside pagination metadata:
{ "data": [ { "id": "c1a2b3c4-...", "properties": { "full_name": "Sarah Chen", "email": "sarah@example.com" } } ], "has_more": true, "next_cursor": "eyJrIjoi..."}To fetch the next page, pass next_cursor back unchanged as cursor. When cursor is set, page and limit are derived from the cursor and any explicit values are ignored. has_more is false on the last page, at which point next_cursor is null.
{ "query": { "select": ["full_name"], "cursor": "eyJrIjoi..." }}Set include_total: true (a sibling of query, not inside it) to add an unpaginated total to the response. It costs an extra pass over the result set, so prefer GET /v2/prism/{teamId}/{objectType}/count for unfiltered totals.
:::caution
page still works but is deprecated — page-number pagination drifts under concurrent writes. Use cursor for anything other than a one-shot export.
:::
Scoping to a list
Section titled “Scoping to a list”Pass list_id — a UUID — to scope results to a specific list (e.g. your fundraising pipeline vs. your investor network):
{ "query": { "select": ["full_name", "stage"], "list_id": "3f9a1c42-5b7e-4d18-9a2f-8c1e6b0d7f35" }}query rejects unknown properties, so a misspelled or renamed key returns 400 rather than being silently ignored.