Skip to content
Stand with Ukraine flag

Entity Data Query API

The Entity Data Query API is a powerful, flexible way to search, filter, and subscribe to entities in ThingsBoard. It is used by:

  • REST APIPOST /api/entitiesQuery/find and POST /api/entitiesQuery/count
  • WebSocket APIENTITY_DATA and ENTITY_COUNT command types

Both interfaces accept the same query structure, so everything on this page applies to both REST and WebSocket usage. The Alarm Query API extends this format with alarm-specific fields.

An EntityDataQuery has five fields:

FieldRequiredDescription
entityFilterYesSelects which entities to include — by type, name, relation, profile, or explicit list
keyFiltersNoNarrows results by attribute or telemetry values (e.g. temperature > 50)
pageLinkYesPagination, sorting, and text search
entityFieldsNoEntity fields to return (name, type, label, etc.)
latestValuesNoLatest attribute or telemetry keys to return with each entity
Minimal query — all devices, sorted by creation time
{
"entityFilter": {
"type": "entityType",
"entityType": "DEVICE"
},
"pageLink": {
"pageSize": 10,
"page": 0,
"sortOrder": {
"key": { "type": "ENTITY_FIELD", "key": "createdTime" },
"direction": "DESC"
}
},
"entityFields": [
{ "type": "ENTITY_FIELD", "key": "name" },
{ "type": "ENTITY_FIELD", "key": "type" }
],
"latestValues": [
{ "type": "TIME_SERIES", "key": "temperature" },
{ "type": "ATTRIBUTE", "key": "active" }
],
"keyFilters": []
}
{
"data": [
{
"entityId": { "entityType": "DEVICE", "id": "784f394c-42b6-435a-983c-b7beff2784f9" },
"latest": {
"ENTITY_FIELD": {
"name": { "ts": 0, "value": "Thermostat A" },
"type": { "ts": 0, "value": "thermostat" }
},
"TIME_SERIES": {
"temperature": { "ts": 1704067260000, "value": "22.5" }
},
"ATTRIBUTE": {
"active": { "ts": 1704067200000, "value": "true" }
}
}
}
],
"totalPages": 1,
"totalElements": 1,
"hasNext": false
}

Each value is returned as a { ts, value } pair where ts is the Unix timestamp in milliseconds and value is always a string.

The entityFilter field selects which entities to include in the query. Each filter has a type discriminator and type-specific fields.

All entities of a given type.

{ "type": "entityType", "entityType": "DEVICE" }

Supported entity types: DEVICE, ASSET, ENTITY_VIEW, TENANT, CUSTOMER, USER, DASHBOARD, EDGE, RULE_CHAIN, RULE_NODE.

One specific entity by ID.

{
"type": "singleEntity",
"singleEntity": { "entityType": "DEVICE", "id": "784f394c-42b6-435a-983c-b7beff2784f9" }
}

A list of specific entities.

{
"type": "entityList",
"entityType": "DEVICE",
"entityList": [
"784f394c-42b6-435a-983c-b7beff2784f9",
"5ab3c1e0-1234-5678-abcd-ef0123456789"
]
}

Entities whose name starts with the given string (case-insensitive prefix match).

{
"type": "entityName",
"entityType": "DEVICE",
"entityNameFilter": "Thermostat"
}

Devices belonging to specific device profiles.

{
"type": "deviceType",
"deviceTypes": ["thermostat", "sensor"],
"deviceNameFilter": ""
}

Set deviceNameFilter to a pattern to narrow by name within those profiles, or leave empty for all.

Assets belonging to specific asset profiles.

{
"type": "assetType",
"assetTypes": ["building", "floor"],
"assetNameFilter": ""
}

Edge instances by edge type.

{
"type": "edgeType",
"edgeTypes": ["factory-edge"],
"edgeNameFilter": ""
}

Entity views by type.

{
"type": "entityViewType",
"entityViewTypes": ["temperature-view"],
"entityViewNameFilter": ""
}

Entities related to a root entity via the relation graph. This is the most flexible filter — it traverses the relation tree to any depth.

{
"type": "relationsQuery",
"rootEntity": { "entityType": "ASSET", "id": "building-uuid" },
"direction": "FROM",
"maxLevel": 2,
"fetchLastLevelOnly": false,
"filters": [
{
"relationType": "Contains",
"entityTypes": ["DEVICE", "ASSET"]
}
]
}
FieldDescription
rootEntityStarting entity for the traversal
directionFROM — follow outgoing relations; TO — follow incoming relations
maxLevelMaximum traversal depth (0 = unlimited)
fetchLastLevelOnlyWhen true, only return entities at the deepest level reached
filtersArray of relation type + entity type pairs to follow

Multi-root variant — start from multiple entities of the same type:

{
"type": "relationsQuery",
"isMultiRoot": true,
"multiRootEntitiesType": "ASSET",
"multiRootEntityIds": ["building-1-uuid", "building-2-uuid"],
"direction": "FROM",
"maxLevel": 1,
"filters": [{ "relationType": "Contains", "entityTypes": ["DEVICE"] }]
}

Devices reachable via the relation graph from a root entity.

{
"type": "deviceSearchQuery",
"rootEntity": { "entityType": "ASSET", "id": "building-uuid" },
"relationType": "Contains",
"direction": "FROM",
"maxLevel": 2,
"fetchLastLevelOnly": false,
"deviceTypes": ["thermostat"]
}

Same as deviceSearchQuery but for assets.

{
"type": "assetSearchQuery",
"rootEntity": { "entityType": "TENANT", "id": "tenant-uuid" },
"relationType": "Contains",
"direction": "FROM",
"maxLevel": 1,
"fetchLastLevelOnly": false,
"assetTypes": ["building"]
}

Same pattern for edges: edgeTypes field instead of deviceTypes.

Same pattern for entity views: entityViewTypes field.

Returns the API usage state entity. Used internally for monitoring.

{ "type": "apiUsageState" }

Key filters narrow results based on attribute or telemetry values. For example, return only devices where temperature > 50 or active == true.

Each key filter has three parts:

FieldDescription
keyWhich key to filter on (EntityKey with type and key)
valueTypeData type: STRING, NUMERIC, BOOLEAN, or DATE_TIME
predicateComparison operation and value
Filter: temperature > 50
{
"keyFilters": [
{
"key": { "type": "TIME_SERIES", "key": "temperature" },
"valueType": "NUMERIC",
"predicate": {
"type": "NUMERIC",
"operation": "GREATER",
"value": { "defaultValue": 50.0 }
}
}
]
}
typeDescription
ENTITY_FIELDBuilt-in entity fields: name, type, label, createdTime, etc.
ATTRIBUTEAny attribute (searches all scopes)
CLIENT_ATTRIBUTEClient-side attributes only
SHARED_ATTRIBUTEShared attributes only
SERVER_ATTRIBUTEServer-side attributes only
TIME_SERIESLatest telemetry value
OperationDescription
EQUALExact match
NOT_EQUALNot equal
STARTS_WITHPrefix match
ENDS_WITHSuffix match
CONTAINSSubstring match
NOT_CONTAINSDoes not contain
INValue is in a comma-separated list
NOT_INValue is not in a comma-separated list
Filter: device name starts with 'Thermo'
{
"key": { "type": "ENTITY_FIELD", "key": "name" },
"valueType": "STRING",
"predicate": {
"type": "STRING",
"operation": "STARTS_WITH",
"value": { "defaultValue": "Thermo" },
"ignoreCase": true
}
}

ignoreCase makes the comparison case-insensitive.

OperationDescription
EQUALEqual
NOT_EQUALNot equal
GREATERGreater than
LESSLess than
GREATER_OR_EQUALGreater than or equal
LESS_OR_EQUALLess than or equal
Filter: battery level ≤ 20
{
"key": { "type": "TIME_SERIES", "key": "batteryLevel" },
"valueType": "NUMERIC",
"predicate": {
"type": "NUMERIC",
"operation": "LESS_OR_EQUAL",
"value": { "defaultValue": 20.0 }
}
}
OperationDescription
EQUALEqual
NOT_EQUALNot equal
Filter: active == true
{
"key": { "type": "ATTRIBUTE", "key": "active" },
"valueType": "BOOLEAN",
"predicate": {
"type": "BOOLEAN",
"operation": "EQUAL",
"value": { "defaultValue": true }
}
}

Combine multiple predicates with boolean logic:

Filter: temperature > 50 AND humidity > 70
{
"key": { "type": "TIME_SERIES", "key": "temperature" },
"valueType": "NUMERIC",
"predicate": {
"type": "COMPLEX",
"operation": "AND",
"predicates": [
{
"type": "NUMERIC",
"operation": "GREATER",
"value": { "defaultValue": 50.0 }
},
{
"key": { "type": "TIME_SERIES", "key": "humidity" },
"valueType": "NUMERIC",
"type": "NUMERIC",
"operation": "GREATER",
"value": { "defaultValue": 70.0 }
}
]
}
}

Instead of hardcoding filter values, you can resolve them from the current tenant, customer, or user attributes at query time. This is primarily used in dashboard widgets where the same widget serves different users.

Filter: temperature > (threshold from tenant attribute)
{
"key": { "type": "TIME_SERIES", "key": "temperature" },
"valueType": "NUMERIC",
"predicate": {
"type": "NUMERIC",
"operation": "GREATER",
"value": {
"defaultValue": 50.0,
"dynamicValue": {
"sourceType": "CURRENT_TENANT",
"sourceAttribute": "temperatureThreshold"
}
}
}
}
sourceTypeResolves attribute from
CURRENT_TENANTCurrent tenant
CURRENT_CUSTOMERCurrent customer
CURRENT_USERCurrent user
CURRENT_DEVICECurrent device (when context is available)

Resolution priority: If dynamicValue resolves successfully, it overrides defaultValue. If it fails (e.g. attribute not found), defaultValue is used as the fallback.

The pageLink controls pagination, sorting, and text search.

FieldTypeDefaultDescription
pageSizeintNumber of results per page (required)
pageint0Zero-based page number
textSearchstringFree-text search across entity names
sortOrderobjectSort key and direction
dynamicbooleanfalseWhen true (WebSocket only), the subscription re-evaluates the query on every update to keep pagination and filtering current
Sort by name ascending with text search
{
"pageSize": 20,
"page": 0,
"textSearch": "thermo",
"sortOrder": {
"key": { "type": "ENTITY_FIELD", "key": "name" },
"direction": "ASC"
}
}

You can sort by any EntityKey — including attributes and telemetry:

Sort by latest temperature value
{
"pageSize": 10,
"page": 0,
"sortOrder": {
"key": { "type": "TIME_SERIES", "key": "temperature" },
"direction": "DESC"
}
}

entityFields specifies built-in entity properties to return:

KeyDescription
nameEntity name
typeEntity sub-type (device profile name, asset profile name)
labelEntity label
createdTimeCreation timestamp
additionalInfoAdditional info JSON

latestValues specifies the latest attribute or telemetry values to include with each entity:

"latestValues": [
{ "type": "ATTRIBUTE", "key": "active" },
{ "type": "SERVER_ATTRIBUTE", "key": "inactivityAlarmTime" },
{ "type": "TIME_SERIES", "key": "temperature" }
]

POST /api/entitiesQuery/find

Send the full EntityDataQuery as the request body. Returns a paginated PageData<EntityData> response.

Terminal window
curl -s -X POST "$TB_URL/api/entitiesQuery/find" \
-H "Content-Type: application/json" \
-H "X-Authorization: $AUTH" \
-d '{
"entityFilter": { "type": "deviceType", "deviceTypes": ["thermostat"], "deviceNameFilter": "" },
"pageLink": { "pageSize": 10, "page": 0, "sortOrder": { "key": { "type": "ENTITY_FIELD", "key": "name" }, "direction": "ASC" } },
"entityFields": [{ "type": "ENTITY_FIELD", "key": "name" }],
"latestValues": [{ "type": "TIME_SERIES", "key": "temperature" }],
"keyFilters": []
}'

POST /api/entitiesQuery/count

Send an EntityCountQuery (only entityFilter and keyFilters — no pagination or fields). Returns a number.

Terminal window
curl -s -X POST "$TB_URL/api/entitiesQuery/count" \
-H "Content-Type: application/json" \
-H "X-Authorization: $AUTH" \
-d '{
"entityFilter": { "type": "deviceType", "deviceTypes": ["thermostat"], "deviceNameFilter": "" },
"keyFilters": []
}'

POST /api/v2/entitiesQuery/find/keys

Returns the list of attribute and telemetry keys available for entities matching the query. Useful for building dynamic UIs.

Query parameterDefaultDescription
includeTimeseriestrueInclude time-series keys
includeAttributestrueInclude attribute keys
scopesallComma-separated attribute scopes: SERVER_SCOPE, SHARED_SCOPE, CLIENT_SCOPE

Use the same query structure inside ENTITY_DATA and ENTITY_COUNT WebSocket commands. The WebSocket subscription provides real-time updates — the server pushes changes as they happen.

WebSocket entity data subscription
{
"cmds": [
{
"cmdId": 1,
"type": "ENTITY_DATA",
"query": {
"entityFilter": { "type": "deviceType", "deviceTypes": ["thermostat"], "deviceNameFilter": "" },
"pageLink": { "pageSize": 10, "page": 0, "sortOrder": { "key": { "type": "ENTITY_FIELD", "key": "name" }, "direction": "ASC" } },
"entityFields": [{ "type": "ENTITY_FIELD", "key": "name" }],
"latestValues": [{ "type": "TIME_SERIES", "key": "temperature" }],
"keyFilters": []
}
}
]
}

Additional WebSocket commands:

FieldDescription
latestCmdSubscribe to latest values for specific keys: { "keys": [{ "type": "TIME_SERIES", "key": "temperature" }] }
tsCmdSubscribe to a rolling time window: { "keys": ["temperature"], "startTs": 1704067200000, "timeWindow": 3600000 }
historyCmdOne-time fetch of historical data: { "keys": ["temperature"], "startTs": 1704067200000, "endTs": 1704153600000, "interval": 60000, "limit": 100, "agg": "AVG" }

See the WebSocket API reference for connection, authentication, and the full message flow.

Find all devices where the batteryLevel telemetry is below 20%:

{
"entityFilter": { "type": "entityType", "entityType": "DEVICE" },
"pageLink": {
"pageSize": 100,
"page": 0,
"sortOrder": {
"key": { "type": "TIME_SERIES", "key": "batteryLevel" },
"direction": "ASC"
}
},
"entityFields": [
{ "type": "ENTITY_FIELD", "key": "name" },
{ "type": "ENTITY_FIELD", "key": "type" }
],
"latestValues": [
{ "type": "TIME_SERIES", "key": "batteryLevel" }
],
"keyFilters": [
{
"key": { "type": "TIME_SERIES", "key": "batteryLevel" },
"valueType": "NUMERIC",
"predicate": {
"type": "NUMERIC",
"operation": "LESS",
"value": { "defaultValue": 20.0 }
}
}
]
}

Find thermostats where active attribute is true and temperature exceeds 30:

{
"entityFilter": {
"type": "deviceType",
"deviceTypes": ["thermostat"],
"deviceNameFilter": ""
},
"pageLink": {
"pageSize": 50,
"page": 0,
"sortOrder": {
"key": { "type": "TIME_SERIES", "key": "temperature" },
"direction": "DESC"
}
},
"entityFields": [
{ "type": "ENTITY_FIELD", "key": "name" }
],
"latestValues": [
{ "type": "TIME_SERIES", "key": "temperature" },
{ "type": "ATTRIBUTE", "key": "active" }
],
"keyFilters": [
{
"key": { "type": "ATTRIBUTE", "key": "active" },
"valueType": "BOOLEAN",
"predicate": {
"type": "BOOLEAN",
"operation": "EQUAL",
"value": { "defaultValue": true }
}
},
{
"key": { "type": "TIME_SERIES", "key": "temperature" },
"valueType": "NUMERIC",
"predicate": {
"type": "NUMERIC",
"operation": "GREATER",
"value": { "defaultValue": 30.0 }
}
}
]
}

Find all devices connected to a specific building asset via Contains relations, up to 3 levels deep:

{
"entityFilter": {
"type": "relationsQuery",
"rootEntity": { "entityType": "ASSET", "id": "building-uuid" },
"direction": "FROM",
"maxLevel": 3,
"fetchLastLevelOnly": false,
"filters": [
{ "relationType": "Contains", "entityTypes": ["DEVICE"] }
]
},
"pageLink": {
"pageSize": 100,
"page": 0,
"sortOrder": {
"key": { "type": "ENTITY_FIELD", "key": "name" },
"direction": "ASC"
}
},
"entityFields": [
{ "type": "ENTITY_FIELD", "key": "name" },
{ "type": "ENTITY_FIELD", "key": "type" }
],
"latestValues": [
{ "type": "TIME_SERIES", "key": "temperature" }
],
"keyFilters": []
}
  • Alarm Query API — extends this query format with alarm-specific filters
  • WebSocket API — real-time subscriptions using entity data queries
  • REST API — authentication and general API usage