LiveObjects provides a comprehensive REST API that enables you to directly work with objects without using a client SDK.
Authentication
View the REST API authentication documentation for details on how to authenticate your requests.
To use LiveObjects, an API key must have at least the object-subscribe capability. With only this capability, clients will have read-only access, preventing them from publishing operations.
In order to create or update objects, make sure your API key includes both object-subscribe and object-publish capabilities to allow full read and write access.
Data values
When working with objects via the REST API, primitive types and object references are included in request and response bodies under data fields.
The key in the data object indicates the type of the value:
1
2
3
4
5
{ "data": { "number": 42 } }
{ "data": { "string": "LiveObjects is awesome" } }
{ "data": { "boolean": true } }
{ "data": { "bytes": "TGl2ZU9iamVjdHMgaXMgYXdlc29tZQo=" } }
{ "data": { "objectId": "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669" } }Fetch the channel object
Get the entire object
The following request returns the entire channel object.
curl "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:*****This endpoint returns the compact representation of the channel object.
1
2
3
4
5
6
{
"votes": {
"down": 10,
"up": 5,
}
}Each instance of an object type included is the response is counted as one billable message.
Get the entire object with metadata
You can optionally include additional object metadata in the response by specifying the compact=false query option:
curl "https://main.realtime.ably.net/channels/my-channel/object?compact=false" \
-u demokey:*****1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
"objectId": "root",
"map": {
"semantics": "LWW",
"entries": {
"votes": {
"data": {
"objectId": "map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692",
"map": {
"semantics": "LWW",
"entries": {
"down": {
"data": {
"objectId": "counter:Yj1F_aEX3T2rRkTkra7Aifmlr8PxUWSR3kO3MzxtQto@1760448653393",
"counter": {
"data": {
"number": 5
}
}
}
},
"up": {
"data": {
"objectId": "counter:ibxddWpDjH8R3cvXWWacfe4IVd3DxT_oqkAafhaS68s@1760448646413",
"counter": {
"data": {
"number": 10
}
}
}
}
}
}
}
}
}
}
}Query the object by path
You can return a subset of the channel object by specifying the path query param. For example, to return just the votes LiveMap instance from the root object:
curl "https://main.realtime.ably.net/channels/my-channel/object?path=votes" \
-u demokey:*****1
2
3
4
{
"down": 5,
"up": 10
}Each instance of an object type included is the response is counted as one billable message.
Get an object by id
To fetch a specific instance of an object type on the channel you can specify the object ID in the URL path:
curl "https://main.realtime.ably.net/channels/my-channel/object/map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692" \
-u demokey:*****This endpoint supports the path and compact query options. Note that the path is evaluated relative to the specified object instance.
1
2
3
4
{
"down": 5,
"up": 10
}The full object response can be retrieved by providing the compact=false query param.
curl "https://main.realtime.ably.net/channels/my-channel/object/map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692?compact=false" \
-u demokey:*****1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"objectId": "map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692",
"map": {
"semantics": "LWW",
"entries": {
"down": {
"data": {
"objectId": "counter:Yj1F_aEX3T2rRkTkra7Aifmlr8PxUWSR3kO3MzxtQto@1760448653393",
"counter": {
"data": {
"number": 5
}
}
}
},
"up": {
"data": {
"objectId": "counter:ibxddWpDjH8R3cvXWWacfe4IVd3DxT_oqkAafhaS68s@1760448646413",
"counter": {
"data": {
"number": 10
}
}
}
}
}
}
}The path param can also be provided here to return a subset of the channel object starting from the specified object ID.
curl "https://main.realtime.ably.net/channels/my-channel/object/map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692?path=votes" \
-u demokey:*****1
2
3
4
{
"down": 5,
"up": 10
}Each instance of an object type included is the response is counted as one billable message.
Cyclic references
For both the full object and the compact response formats cyclic references in the channel object will be included as a reference to the object ID rather than including the same object instance in the response more than once.
For example, if we created a cycle in the channel object by adding a reference to the root object in the votes LiveMap instance with the following operation:
curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:***** \
-H "Content-Type: application/json" \
-d '{
"operation": "MAP_SET",
"objectId": "map:qZXBk8xaGqf4kwLSlR7Tj0Eqhd48WDFfb3gTAbj194k@1760448597692",
"data": {"key": "myRoot", "value": {"objectId": "root"}}
}'The response will handle the cyclic reference by including the myRoot key in the response as a reference to the object ID of the root object:
curl "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:*****1
2
3
4
5
6
7
8
9
{
"votes": {
"down": 5,
"up": 10
"myRoot": {
"objectId": "root"
},
}
}Publishing operations
All operations are published to the same endpoint. The request body specifies:
- The type of operation to publish
- The object(s) to which the operation should be applied
- The operation payload
The request body is of the form:
1
2
3
4
5
6
{
"operation": "<operationType>",
"objectId": "<objectId>",
"path": "<path>",
"data": "<data>"
}Example setting a key on the channel object:
curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:***** \
-H "Content-Type: application/json" \
-d '{
"operation": "MAP_SET",
"path": "",
"data": {
"key" : "myKey",
"value": {"string": "myValue"}
}
}'The operationType is a string that specifies the type of operation to publish and must be one of:
| Operation | Description |
|---|---|
MAP_CREATE | Creates LiveMap object. |
MAP_SET | Set the value of a key in a LiveMap object. |
MAP_REMOVE | Tombstone a key in a LiveMap object. |
COUNTER_CREATE | Creates a LiveCounter object. |
COUNTER_INC | Increments or decrements the value of a LiveCounter object. |
Either the objectId or path fields are used to specify the target object(s) for the operation.
The operation payload is provided in the data in accordance with the specified operation type.
Each published operation counts as one billable message.
Update a specific object instance
To perform operations on a specific object instance, you need to provide its object ID in the request body:
curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:***** \
-H "Content-Type: application/json" \
-d '{
"operation": "COUNTER_INC",
"objectId": "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269",
"data": {"number":1}
}'The response includes the ID of the published operation message, the channel and a list of object IDs that were affected by the operation:
1
2
3
4
5
{
"messageId": "TJPWHhMTrF:0",
"channel": "my-channel",
"objectIds": ["counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269"]
}Update an object by path
Path operations provide a convenient way to target objects based on their location in the channel object.
Paths are expressed relative to the structure of the object as defined by the compact view of the channel object.
For example, given the following compact view of the channel object:
The following example increments the LiveCounter instance stored at the up key on the votes LiveMap object:
curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:***** \
-H "Content-Type: application/json" \
-d '{
"operation": "COUNTER_INC",
"path": "votes.up",
"data": { "number": 1 }
}'You can use wildcards in paths to target multiple objects at once. To increment all LiveCounter instances in the votes LiveMap instance:
curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:***** \
-H "Content-Type: application/json" \
-d '{
"operation": "COUNTER_INC",
"path": "votes.*",
"data": { "number": 1 }
}'The response includes the IDs of each of the affected object instances:
1
2
3
4
5
6
7
8
{
"messageId": "0Q1w-LpA11:0",
"channel": "my-channel",
"objectIds": [
"counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269",
"counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669"
]
}Wildcards can be included at the end or in the middle of paths and will match exactly one level in the channel object. For example, given the following compact view of the channel object:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"posts": {
"post1": {
"votes": {
"up": 5,
"down": 10
}
},
"post2": {
"votes": {
"up": 5,
"down": 10
}
}
}
}The following example increments the upvote LiveCounter instances for all posts in the posts LiveMap instance:
curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:***** \
-H "Content-Type: application/json" \
-d '{
"operation": "COUNTER_INC",
"path": "posts.*.votes.up",
"data": { "number": 1 }
}'If your LiveMap keys contain periods, you can escape them with a backslash. The following example increments the upvote LiveCounter instance for a post with the key post.123:
curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:***** \
-H "Content-Type: application/json" \
-d '{
"operation": "COUNTER_INC",
"path": "posts.post\.123.votes.up",
"data": { "number": 1 }
}'Creating objects
Use the MAP_CREATE and COUNTER_CREATE operations to create new objects. You can optionally specify an initial value for the object in the data field when creating it.
For MAP_CREATE, the data field should be a JSON object that contains the initial entries for the LiveMap instance:
1
2
3
4
5
6
7
8
{
"operation": "MAP_CREATE",
"data": {
"title": {"string": "LiveObjects is awesome"},
"createdAt": {"number": 1745835181122},
"isPublished": {"boolean": true}
}
}For COUNTER_CREATE, the data field should be a JSON object that contains the initial value for the LiveCounter instance:
1
2
3
4
{
"operation": "COUNTER_CREATE",
"data": { "number": 5 }
}When you create a new object it is important that the new object instance is assigned to the channel object so that it is reachable.
The simplest way to do this is to use the path field in the request body. The path is relative to the root object and specifies where in the channel object the new object should be created.
The following example creates a new LiveMap instance and assigns it to the posts LiveMap instance on the root object under the key post1:
curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:***** \
-H "Content-Type: application/json" \
-d '{
"operation": "MAP_CREATE",
"path": "posts.post1",
"data": {
"title": {"string": "LiveObjects is awesome"},
"createdAt": {"number": 1745835181122},
"isPublished": {"boolean": true}
}
}'The following example shows how to update a key and value in a LiveMap:
curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:***** \
-H "Content-Type: application/json" \
-d '{
"operation": "MAP_SET",
"path": "posts.post1",
"data": {
"key" : "title",
"value": {"string": "LiveObjects is still awesome"}
}
}'When using the path specifier with a COUNTER_CREATE or MAP_CREATE operation, the server constructs two operations which are published as a batch:
- A
MAP_CREATEorCOUNTER_CREATEoperation used to create the new object - A
MAP_SEToperation used to assign the new object to theLiveMapinstance specified by thepath
Therefore the response will include the object IDs of all objects affected by the resulting set of operations:
1
2
3
4
5
6
7
8
{
"messageId": "mkfjWU2jju:0",
"channel": "my-channel",
"objectIds": [
"map:cRCKx-eev7Tl66jGfl1SkZh_uEMo6F5jyV0B7mUn4Zs@1745835549101",
"map:a_oQqPYUGxi95_Cn0pWcsoeBlHZZtVW5xKIw0hnJCZs@1745835547258"
]
}Removing objects
There is no explicit delete operation for objects themselves. Objects that are not reachable from the root map will be eligible for garbage collection.
Remove a reference to a nested object in a LiveMap instance using the MAP_REMOVE operation:
curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:***** \
-H "Content-Type: application/json" \
-d '{
"operation": "MAP_REMOVE",
"objectId": "root",
"data": {"key": "posts"}
}'If no other references to the object exist, it will no longer be reachable from the root object and will be eligible for garbage collection.
Batch operations
You can group several operations into a single request by sending an array of operations.
All operations inside the array form a batch operation which is published as a single message. All operations in the batch are processed as a single atomic unit. Batch operations therefore count as one billable message.
The following example shows how to increment two distinct LiveCounter instances in a single batch operation:
curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:***** \
-H "Content-Type: application/json" \
-d '[
{
"operation": "COUNTER_INC",
"objectId": "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269",
"data": {"number": 1}
},
{
"operation": "COUNTER_INC",
"objectId": "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669",
"data": {"number": 1}
}
]'Idempotent operations
Publish operations idempotently in the same way as for idempotent message publishing by specifying an id for the operation message:
curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:***** \
-H "Content-Type: application/json" \
-d '{
"id": "my-idempotency-key",
"operation": "COUNTER_INC",
"objectId": "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269",
"data": {"number": 1}
}'For batch operations, use the format <baseId>:<index> where the index is the zero-based index of the operation in the array:
curl -X POST "https://main.realtime.ably.net/channels/my-channel/object" \
-u demokey:***** \
-H "Content-Type: application/json" \
-d '[
{
"id": "my-idempotency-key:0",
"operation": "COUNTER_INC",
"objectId": "counter:iVji62_MW_j4dShuJbr2fmsP2D8MyCs6tFqON9-xAkc@1745828645269",
"data": {"number": 1}
},
{
"id": "my-idempotency-key:1",
"operation": "COUNTER_INC",
"objectId": "counter:JbZYiHnw0ORAyzzLSQahVik31iBDL_ehJNpTEF3qwg8@1745828651669",
"data": {"number": 1}
}
]'