Query records – POST /api/v1/records/{entityId}/query¶
Searches the records of an entity with a filter, sorts them, returns selected fields and pages through the result. The filter has the same building blocks as a query in the Admin Studio – groups, field clauses, related clauses, date part clauses and full text clauses. This page is the reference for the operation.
1. At a glance¶
| Purpose | Find records by their values |
| Method and path | POST /api/v1/records/{entityId}/query |
| Typical use | Find a customer by customer number; fetch all orders changed in the last day; export in pages |
| Permissions | Valid token. Only records within the "Read Limits" of the API user are found and counted. |
| Success | 200 OK, a page of records and the total number of hits |
| Related | Queries (the same clauses in the Admin Studio), Fields and values |
2. Request¶
Path parameters¶
| Parameter | Meaning | Required |
|---|---|---|
entityId |
ID of the entity that is searched | Yes |
Body¶
{
"query": { "type": "field", "field": "Status", "operator": "in", "value": ["Open", "InProgress"] },
"sort": [ { "field": "CreatedAt", "dir": "desc" } ],
"fields": [ "OrderNumber", "Status", "CreatedAt" ],
"page": { "limit": 100, "skip": 0 }
}
| Property | Meaning | Default |
|---|---|---|
query |
The filter: one clause (section 3). Use a group clause to combine several. |
none – all readable records |
sort |
Sort rules, applied in the order given | none – the order of the database, which can change |
sort[].field |
Field to sort by | required |
sort[].dir |
asc or desc |
asc |
fields |
The fields to return. The record ID is always returned. | none – all fields |
page.limit |
Number of records to return, from 1 to 1000. Larger values are reduced to 1000. | 50 |
page.skip |
Number of records to skip | 0 |
An empty body {} returns the first 50 readable records.
3. Clauses¶
Every clause is a JSON object with a type:
type |
Checks | Section |
|---|---|---|
group |
Combines several clauses with and or or |
3.1 |
field |
A field against a value or another field | 3.2 |
related |
How many related records meet conditions | 3.3 |
datePart |
A part of a date, or a time range relative to now | 3.4 |
fullText |
Words in full-text-indexed fields | 3.5 |
Important: type must be the first property of the clause object and is written exactly as in
the table (fullText, not fulltext). Otherwise the request is rejected with 400 (see
Errors, section 4). Operator, gate, date part and unit values, in contrast, ignore upper
and lower case: equals, Equals and EQUALS are the same.
3.1 Group¶
{
"type": "group",
"gate": "or",
"clauses": [
{ "type": "field", "field": "Name", "operator": "like", "value": "%ACME%" },
{
"type": "group",
"gate": "and",
"clauses": [
{ "type": "field", "field": "IsActive", "operator": "equals", "value": true },
{ "type": "field", "field": "CreditLimit", "operator": "greaterThan", "value": 10000 }
]
}
]
}
| Property | Meaning | Default |
|---|---|---|
gate |
and – all clauses must hold; or – one is enough |
and |
clauses |
The clauses of the group. Groups can be nested to any depth. | empty – no condition |
3.2 Field clause¶
{ "type": "field", "field": "CreditLimit", "operator": "greaterThanOrEqual", "value": 10000 }
| Property | Meaning | Default |
|---|---|---|
field |
The field that is compared (see Fields and values) | required |
relationshipId |
ID of a relationship to exactly one record (to-one). field is then a field of the related record, for example the "Country" of the customer of an order. |
none |
operator |
See the table below | required |
value |
The value to compare with, written as for the field type. A JSON array for in and notIn. Not needed for empty and notEmpty. |
– |
valueField |
Compare with another field of the same record instead of a value. Takes precedence over value. |
none |
operator |
True when |
|---|---|
equals, notEquals |
The field equals / differs from the value |
lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual |
The field is smaller / greater than the value |
like, notLike |
The field matches / does not match a pattern, ignoring upper and lower case. Add the wildcards yourself: % for any text, _ for one character. "%ACME%" means "contains ACME"; "ACME" only matches exactly "ACME". |
empty, notEmpty |
The field has no value / has a value (see Queries, "Empty values") |
in, notIn |
The field equals one / none of the values of the array, for example ["Open", "New"] |
The value is converted to the type of the field. "value": "10000" for a number field is the same as
10000; "abc" for a number field leads to 400 invalid_field_value. An equality comparison on a
date and time field compares the exact moment; use a date part clause to compare by day.
3.3 Related clause¶
Checks the related records of each record, for example "customers with at least two open invoices".
{
"type": "related",
"relationshipDefId": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
"operator": "atLeastXContains",
"numberRecords": 2,
"clauses": [
{ "type": "field", "field": "Status", "operator": "notEquals", "value": "Paid" }
]
}
| Property | Meaning | Default |
|---|---|---|
relationshipDefId |
ID of the relationship from the searched entity to the related records | required |
operator |
atLeastXContains, atMostXContains, exactlyXContains, notExactlyXContains, allContains, noneContains – see Queries, section 5 |
required |
numberRecords |
The X of the operators that need one | 0 |
clauses |
Conditions on the related records. Fields in them are fields of the related entity. | empty – every related record counts |
3.4 Date part clause¶
Checks a part of a date, or whether a date lies in a time range relative to now.
{ "type": "datePart", "field": "InvoiceDate", "datePart": "year", "operator": "equals", "value": 2026 }
{ "type": "datePart", "field": "CreatedAt", "operator": "is", "unit": "withInXDays", "number": 7 }
| Property | Meaning | Default |
|---|---|---|
field |
A date or date and time field | required |
relationshipId |
As in a field clause | none |
datePart |
date, dateTime, time, day (day of the year), dayOfWeek, dayOfMonth, week, month, quarter, year, hour (0–23) |
date |
operator |
equals, notEquals, lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual compare the part with value. is and isNot check the time range from unit and number. empty and notEmpty check whether the field has a value. |
required |
value |
The value the part is compared with, for example 2026 for year or "2026-03-04" for date |
– |
valueField |
Compare with another field of the same record instead | none |
unit |
Only for is / isNot: now, withInXMinutes, withInXHours, withInXDays, withInXWeeks, withInXMonths, withInXYears, beforeXMinutes, beforeXHours, beforeXDays, beforeXWeeks, beforeXMonths, beforeXYears |
required for is / isNot |
number |
The X of the unit | 0 |
withInX… means "within the last X units", beforeX… "more than X units ago". Ranges are measured
from the current time of the database server. like, notLike, in and notIn are accepted but
add no usable condition. More on weeks, days of the week and ranges: Queries,
section 6.
3.5 Full text clause¶
Searches words in several fields at once.
{ "type": "fullText", "fields": ["Subject", "Description"], "value": "printer toner", "negate": false }
| Property | Meaning | Default |
|---|---|---|
fields |
The fields that are searched – fields marked "Use in Full Text Search" | required, at least one |
value |
The search words. Every word must occur. | required, not empty |
negate |
true finds the records that do not contain the words |
false |
Full text clauses need an entity with "Enable Full Text Search" and the full text search of the database (see Entities and fields).
4. Response¶
200 OK:
{
"entityDefId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"entityName": "Order",
"totalRecords": 137,
"skip": 0,
"limit": 100,
"records": [
{
"entityDefId": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"entityName": "Order",
"entityRecId": "e5f6a7b8-c9d0-4e1f-a2b3-c4d5e6f7a8b9",
"fields": [
{ "fieldDefId": "c3d4e5f6-a7b8-4c9d-8e0f-1a2b3c4d5e6f", "field": "OrderNumber", "storageName": "OrderNumber", "type": "String", "value": "A-20417" },
{ "fieldDefId": "a0b1c2d3-e4f5-4a6b-9c7d-8e9f0a1b2c3d", "field": "Status", "storageName": "Status", "type": "String", "value": "Open" },
{ "fieldDefId": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a", "field": "CreatedAt", "storageName": "CreatedAt", "type": "DateTime", "value": "2026-10-19T16:48:03" },
{ "fieldDefId": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f", "field": "RecId", "storageName": "RecId", "type": "Guid", "value": "e5f6a7b8-c9d0-4e1f-a2b3-c4d5e6f7a8b9" }
],
"relationships": null
}
]
}
| Property | Meaning |
|---|---|
totalRecords |
Number of all hits, independent of paging |
skip |
The applied skip |
limit |
The applied limit – lower than requested if more than 1000 was asked for |
records |
The records of this page, in the form described in Fields and values; only the fields from fields plus the record ID |
No hits is not an error: totalRecords is 0 and records is empty.
5. Behaviour¶
- The request is turned into a query and evaluated like a query from the Admin Studio, including links to related entities. The "Read Limits" of the API user are added to it.
- Values in the request are never evaluated as expressions.
totalRecordsand the page are read in two steps. If records change in between, the count and the page can differ slightly.
Paging through many records¶
- Always send a
sort– without it the order is not fixed, and pages can overlap or miss records. Sort by a field that does not change, for example "CreatedAt" and then "RecId". - Start with
skip0 and thelimityou want (at most 1000). - Increase
skipbylimituntilskipis equal to or greater thantotalRecords.
Example: totalRecords is 2350, limit 1000: requests with skip 0, 1000 and 2000. The last
page contains 350 records.
6. Status and error codes¶
| HTTP status | error |
Cause |
|---|---|---|
| 200 | – | The query ran |
| 400 | field_missing, field_not_found, field_ambiguous |
A field could not be identified |
| 400 | invalid_field_value |
A value does not fit the field type; in/notIn without an array |
| 400 | invalid_operator |
Unknown or missing operator |
| 400 | invalid_gate |
gate is neither and nor or |
| 400 | invalid_sort_direction |
dir is not asc or desc |
| 400 | invalid_date_part, invalid_date_part_unit |
Unknown datePart or unit |
| 400 | relationship_missing, relationship_not_found, relationship_invalid |
Problem with relationshipDefId / relationshipId |
| 400 | value_missing |
A full text clause without value |
| 400 | – (validation details) | Invalid JSON, missing or unknown type – see Errors, section 4 |
| 401 | – | No valid token |
| 404 | entity_not_found |
No entity with this ID |
| 500 | query_failed |
The database could not run the query |
The text in errorDescription of a 400 lists the allowed values, for example
'between' is not a valid query operator. Allowed values: None, Equals, NotEquals, ....
7. Examples¶
Find a customer by customer number:
curl -X POST https://nexxcite.example.com/api/v1/records/6a1f0c52-3d4e-4b8a-9c71-2f5e8d0b4a13/query \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "query": { "type": "field", "field": "CustomerNumber", "operator": "equals", "value": "K-1001" },
"fields": ["CustomerNumber", "Name"], "page": { "limit": 1 } }'
Orders changed within the last 24 hours, newest first, 500 per page:
{
"query": { "type": "datePart", "field": "LastModifiedAt", "operator": "is", "unit": "withInXHours", "number": 24 },
"sort": [ { "field": "LastModifiedAt", "dir": "desc" }, { "field": "RecId" } ],
"page": { "limit": 500, "skip": 0 }
}
Active customers in Germany or Austria – through the to-one relationship "Country":
{
"query": {
"type": "group",
"gate": "and",
"clauses": [
{ "type": "field", "field": "IsActive", "operator": "equals", "value": true },
{ "type": "field", "relationshipId": "f6a7b8c9-d0e1-4f2a-b3c4-d5e6f7a8b9c0", "field": "IsoCode", "operator": "in", "value": ["DE", "AT"] }
]
}
}
8. Limitations¶
- At most 1000 records per request.
- No aggregations (sum, count per group) – only
totalRecordsfor the whole filter. - Fields of related records cannot be returned in
fields; use Read a record withrelationshipIdfor single records. - Stored queries from the Admin Studio cannot be run by their ID. Rebuild the filter in the request, or run an action that uses the query.
9. Related pages¶
- Queries – the meaning of every clause and operator
- Fields and values
- Read a record
- Errors