Skip to content
Try for free

ThingsBoard Cloud

Choose your data region

Your data stays in the region you choose, for residency and compliance. No credit card required.

Rather run it yourself? Install on your own servers

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 API — POST /api/entitiesQuery/find and POST /api/entitiesQuery/count
  • WebSocket API — ENTITY_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:

Field Required Description
entityFilter Yes Selects which entities to include — by type, name, relation, profile, or explicit list
keyFilters No Narrows results by attribute or telemetry values (e.g. temperature > 50)
pageLink Yes Pagination, sorting, and text search
entityFields No Entity fields to return (name, type, label, etc.)
latestValues No Latest 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"]
}
]
}
Field Description
rootEntity Starting entity for the traversal
direction FROM — follow outgoing relations; TO — follow incoming relations
maxLevel Maximum traversal depth (0 = unlimited)
fetchLastLevelOnly When true, only return entities at the deepest level reached
filters Array of relation type + entity type pairs to follow

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

{
"type": "relationsQuery",
"multiRoot": 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:

Field Description
key Which key to filter on (EntityKey with type and key)
valueType Data type: STRING, NUMERIC, BOOLEAN, or DATE_TIME
predicate Comparison operation and value
Filter: temperature > 50
{
"keyFilters": [
{
"key": { "type": "TIME_SERIES", "key": "temperature" },
"valueType": "NUMERIC",
"predicate": {
"type": "NUMERIC",
"operation": "GREATER",
"value": { "defaultValue": 50.0 }
}
}
]
}
type Description
ENTITY_FIELD Built-in entity fields: name, type, label, createdTime, etc.
ATTRIBUTE Any attribute (searches all scopes)
CLIENT_ATTRIBUTE Client-side attributes only
SHARED_ATTRIBUTE Shared attributes only
SERVER_ATTRIBUTE Server-side attributes only
TIME_SERIES Latest telemetry value
Operation Description
EQUAL Exact match
NOT_EQUAL Not equal
STARTS_WITH Prefix match
ENDS_WITH Suffix match
CONTAINS Substring match
NOT_CONTAINS Does not contain
IN Value is in a comma-separated list
NOT_IN Value 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.

Operation Description
EQUAL Equal
NOT_EQUAL Not equal
GREATER Greater than
LESS Less than
GREATER_OR_EQUAL Greater than or equal
LESS_OR_EQUAL Less 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 }
}
}
Operation Description
EQUAL Equal
NOT_EQUAL Not 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"
}
}
}
}
sourceType Resolves attribute from
CURRENT_TENANT Current tenant
CURRENT_CUSTOMER Current customer
CURRENT_USER Current user
CURRENT_DEVICE Current 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.

Field Type Default Description
pageSize int — Number of results per page (required)
page int 0 Zero-based page number
textSearch string — Free-text search across entity names
sortOrder object — Sort key and direction
dynamic boolean false When 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:

Key Description
name Entity name
type Entity sub-type (device profile name, asset profile name)
label Entity label
createdTime Creation timestamp
additionalInfo Additional 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 parameter Default Description
includeTimeseries true Include time-series keys
includeAttributes true Include attribute keys
scopes all Comma-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:

Field Description
latestCmd Subscribe to latest values for specific keys: { "keys": [{ "type": "TIME_SERIES", "key": "temperature" }] }
tsCmd Subscribe to a rolling time window: { "keys": ["temperature"], "startTs": 1704067200000, "timeWindow": 3600000 }
historyCmd One-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 }
}

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" }
  • 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