Table of Contents
This document describes the request and response payloads that you’ll send and receive to the Data Store API of Open Cloud.
Authorization
Like all Open Cloud APIs, the Data Store API requires all requests to include the x-api-key
header, which should contain an API key that is in scope for the request. This requires the key be applied to the experience and the data store, and the endpoint operation is permitted. If the key is invalid, 403 Unauthorized
is be returned. For more information on API keys, see the main Open Cloud article.
Throttling
DataStore API has two types of universe level throttling: request-per-minute throttling and throughput throttling. With every universeId
, request-per-minute throttling allows you to send a certain number of requests per minute, and throughput throttling allows you to send a certain amount of data per minute, regardless of the number of API keys.
Unlike the Lua API, these limits currently do not scale according to player count. Exceeding these limits causes 429 Too Many Requests
to be returned.
Request Type | Endpoints | Throttle Limits |
---|---|---|
Write |
|
|
Read |
|
Input Validation
Before a request is accepted, endpoint parameters are validated with regards to formatting and constraints. If a parameter does satisfy the following restrictions, the endpoint returns a 400 Bad Request
. Individual endpoints may have additional requirements beyond these.
-
universeId
(integer)- Should be the value of
game.GameId
, which is visible in the URL on the universe's Configure page. - Be careful not to mistake this with a place ID (
game.PlaceId
), which is visible in the URL when you go to play a game.
- Should be the value of
-
datastoreName
(string):- Length must be 50 bytes or less
- Cannot be null or empty
-
scope
(string): data store scope- Length must be 50 bytes or less
- Defaults to global when not provided
-
entryKey
(string):- Length must be 50 bytes or less
- Cannot be null or empty
-
versionId
/matchVersion
(string): data store entry version -
content
(JSON string): user data- Length must be less than 4 MB
-
content-md5
(string): the base-64 encoded MD5 checksum of content- See the Set Entry example for a way of calculating this using Python
-
roblox-entry-attributes
(JSON object)- Length must be less than 300 bytes
-
roblox-entry-userids
(JSON array of numbers)- No more than 4 user IDs
-
cursor
(string): see Cursors below
Cursors
Endpoints which return lists of data may return a nextPageCursor
string. This indicates that there is more data available in the requested result set. To receive it, provide this string in the cursor
query parameter on a subsequent request. If the cursor parameter is provided but invalid, the endpoint returns 400 Bad Request
.
The format of cursor strings is not defined. You should not interpret or parse them as they may change at any time.
Endpoints
-
All endpoint URLs begin with:
https://apis.roblox.com/datastores/v1/universes/{universeId}
Where{universeId}
is the ID of the universe whose data stores are being accessed. -
Successful responses that contain data have the header
content-type: application/json
- Each endpoint is analogous to a function of the Lua Data Store API which performs the same operation. Links to these functions have been provided for comparison and search convenience.
List DataStores in Universe
Endpoint | GET /standard-datastores |
Description | Returns a list of data stores belonging to a universe. |
API key permission | List DataStores |
Query Params |
|
Response |
200 OK :
{ "datastores": [ DataStore, ... ], "nextPageCursor": string | null }Where DataStore are:
{ "name": string, "createdTime": string } |
Lua Equivalent |
DataStoreService:ListDataStoresAsync
|
Example Help
Expand
curl \
--include \
--location \
--request GET \
"https://apis.roblox.com/datastores/v1/universes/3310576216/standard-datastores" \
--header "x-api-key: ${API_KEY}" \
--get \
-d "prefix=Player" \
-d "limit=5"
Response
HTTP/2 200
date: Thu, 10 Mar 2022 05:32:09 GMT
content-type: application/json; charset=utf-8
content-length: 388
Body
{
"datastores":[
{
"name": "PlayerInventory",
"createdTime": "2022-02-18T22:38:59.9244932Z"
},
{
"name": "PlayerExperience",
"createdTime": "2022-02-18T23:00:10.4773508Z"
},
{
"name": "PlayerWeapons",
"createdTime": "2022-02-18T23:00:22.3725681Z"
},
{
"name": "PlayerArmor",
"createdTime": "2022-02-18T22:59:33.8472882Z"
},
{
"name": "PlayerHP",
"createdTime": "2022-02-18T22:58:47.6904028Z"
}
],
"nextPageCursor": "..."
}
Raw (unformatted)
{"datastores":[{"name":"PlayerInventory","createdTime":"2022-02-18T22:38:59.9244932Z"},{"name":"PlayerExperience","createdTime":"2022-02-18T23:00:10.4773508Z"},{"name":"PlayerWeapons","createdTime":"2022-02-18T23:00:22.3725681Z"},{"name":"PlayerArmor","createdTime":"2022-02-18T22:59:33.8472882Z"},{"name":"PlayerHP","createdTime":"2022-02-18T22:58:47.6904028Z"}],"nextPageCursor":"..."}
List Entries
Endpoint | GET /standard-datastores/datastore/entries |
Description | Returns a list of entry keys within a data store. |
API key permission | List Keys |
Query Params |
|
Response |
200 OK :
{ "keys": [ EntryKey, ... ], "nextPageCursor": string | null }Where EntryKey are:
{ "scope": string, "key": string } |
Lua Equivalent |
DataStore:ListKeysAsync
|
Example Help
Expand
curl \
--include \
--location \
--request GET \
"https://apis.roblox.com/datastores/v1/universes/3310576216/standard-datastores/datastore/entries" \
--header "x-api-key: ${API_KEY}" \
--get \
-d "datastoreName=Coins" \
-d "prefix=" \
-d "limit=5"
Response
HTTP/2 200
date: Thu, 03 Feb 2022 05:32:09 GMT
content-type: application/json; charset=utf-8
content-length: 83
Body
{
"keys":[
{
"key":"269323"
}
],
"nextPageCursor":"eyJ2ZXJzaW9uIjoxLCJjdXJzb3IiOiIzIyJ9"
}
Raw (unformatted)
{"keys":[{"key":"269323"}],"nextPageCursor":"eyJ2ZXJzaW9uIjoxLCJjdXJzb3IiOiIzIyJ9"}
Get Entry
Endpoint | GET /standard-datastores/datastore/entries/entry |
Description | Returns the value and metadata associated with an entry. |
API key permission | Read entries |
Query Params |
|
Response |
|
Lua Equivalent |
GlobalDataStore:GetAsync
|
Example Help
Expand
curl \
--include \
--location \
--request GET \
"https://apis.roblox.com/datastores/v1/universes/3310576216/standard-datastores/datastore/entries/entry" \
--header "x-api-key: ${API_KEY}" \
--get \
-d "datastoreName=Coins" \
-d "entryKey=269323"
Response
HTTP/2 200
date: Thu, 03 Feb 2022 05:32:35 GMT
content-type: application/json
content-length: 3
content-md5: zuYxEhwuySMvOi8CitXImw==
roblox-entry-version: 08D9E6A3F2188CFF.0000000001.08D9E6A3F2188CFF.01
roblox-entry-created-time: 2022-02-02T23:30:06.5388799Z
roblox-entry-version-created-time: 2022-02-02T23:30:06.5388799Z
roblox-entry-userids: []
Body
500
Set Entry
Endpoint | POST /standard-datastores/datastore/entries/entry |
Description | Sets the value, metadata and user IDs associated with an entry. |
API key permission |
Create entry (when creating new entries) Update entry (when updating existing entries) |
Query Params |
matchVersion and exclusiveCreate .
|
Headers |
|
Body | JSON, up to 4 MB |
Response |
200 OK : Returns an EntryVersion { "version": string, "deleted": boolean, "contentLength": number, "createdTime": string, "objectCreatedTime": string } |
Lua Equivalent |
GlobalDataStore:SetAsync
|
Example Help
Calculating the content-md5 header using Python
The hashlib.md5()1 and base64.b64encode() functions are available in Python standard libraries (2.7, 3+):
# With prompts
$ python -c "import base64, hashlib; print('content-md5: ' + str(base64.b64encode(hashlib.md5(bytes(input('content: '), encoding='utf8')).digest()), encoding='utf8'))"
content: 750
content-md5: sTf90fedVsft8zZf6nUg8g==
# Using just stdin and stdout
$ echo "750" | python -c "import base64, hashlib; print(str(base64.b64encode(hashlib.md5(bytes(input(), encoding='utf8')).digest()), encoding='utf8'))"
sTf90fedVsft8zZf6nUg8g==
1This function may not be available in rare FIPS-compliant versions of Python.
Expand
Note: Other examples use –get
and -d
to specify query parameters. However, this endpoint requires those in addition to POST data. To do this with cURL, the query parameters are provided in the URL, and the POST data is provided using –data-ascii
.
curl \
--include \
--location \
--request POST \
"https://apis.roblox.com/datastores/v1/universes/3310576216/standard-datastores/datastore/entries/entry?datastoreName=Coins&entryKey=269323" \
--header "x-api-key: ${API_KEY}" \
--header "content-md5: sTf90fedVsft8zZf6nUg8g==" \
--header "content-type: application/json" \
--header "roblox-entry-userids: [269323]" \
--header "roblox-entry-attributes: {}" \
--data-ascii 750
Response
HTTP/2 200
date: Thu, 03 Feb 2022 06:36:22 GMT
content-type: application/json; charset=utf-8
content-length: 191
Body
{
"version":"08D9E6A3F2188CFF.000000000A.08D9E6DF7EAC7BB5.01",
"deleted":false,
"contentLength":3,
"createdTime":"2022-02-03T06:36:22.6964405Z",
"objectCreatedTime":"2022-02-02T23:30:06.5388799Z"
}
Raw (unformatted)
{"version":"08D9E6A3F2188CFF.000000000A.08D9E6DF7EAC7BB5.01","deleted":false,"contentLength":3,"createdTime":"2022-02-03T06:36:22.6964405Z","objectCreatedTime":"2022-02-02T23:30:06.5388799Z"}
Increment Entry
Endpoint | POST /standard-datastores/datastore/entries/entry/increment |
Description | Increments the value for an entry by a given amount, or create a new entry with that amount. |
API key permission |
Create entry (when creating new entries) Update entry (when updating existing entries) |
Query Params |
|
Headers |
|
Response | Responds with the latest version of the entry after it has been incremented. The format is similar to the response for Get Entry. |
Lua Equivalent |
GlobalDataStore:IncrementAsync
|
Example Help
Expand
curl \
--include \
--location \
--request POST \
"https://apis.roblox.com/datastores/v1/universes/3310576216/standard-datastores/datastore/entries/entry/increment" \
--header "x-api-key: ${API_KEY}" \
--header 'content-length: 0' \
--get \
-d datastoreName=Coins \
-d entryKey=269323 \
-d incrementBy=3
Response
HTTP/2 200
date: Thu, 03 Feb 2022 06:36:05 GMT
content-type: application/json
content-length: 3
content-md5: K4phWUsfTE2wkCqKOVztkw==
roblox-entry-version: 08D9E6A3F2188CFF.0000000009.08D9E6DF74AC5F42.01
roblox-entry-created-time: 2022-02-02T23:30:06.5388799Z
roblox-entry-version-created-time: 2022-02-03T06:36:05.9184962Z
roblox-entry-userids: []
Body
515
Delete Entry
Endpoint | DELETE /standard-datastores/datastore/entries/entry |
Description | Marks the entry as deleted by creating a tombstone version. Entries are deleted permanently after 30 days. |
API key permission | Delete Entry |
Query Params |
|
Response |
204 No Content : The entry was deleted.
|
Lua Equivalent |
GlobalDataStore:RemoveAsync
|
Example Help
Expand
curl \
--include \
--location \
--request DELETE \
"https://apis.roblox.com/datastores/v1/universes/3310576216/standard-datastores/datastore/entries/entry" \
--header "x-api-key: ${API_KEY}" \
--get \
-d datastoreName=Coins \
-d entryKey=269323
Response
HTTP/2 204
date: Thu, 03 Feb 2022 06:38:31 GMT
List Entry Versions
Endpoint | GET /standard-datastores/datastore/entries/entry/versions |
Description | Returns a list of data stores belonging to a universe. |
API key permission | List Versions |
Query Params |
|
Response |
200 OK :{ "versions": [ EntryVersion, ... ], "nextPageCursor": string | null }Where EntryVersion are:
{ "version": string, "deleted": boolean, "contentLength": integer, "createdTime": string, "objectCreatedTime": string } |
Lua Equivalent |
DataStore:ListVersionsAsync
|
Example Help
Expand
curl \
--include \
--location \
--request GET \
"https://apis.roblox.com/datastores/v1/universes/3310576216/standard-datastores/datastore/entries/entry/versions" \
--header "x-api-key: ${API_KEY}" \
--get \
-d "datastoreName=Coins" \
-d "entryKey=269323" \
-d "limit=5"
Response
HTTP/2 200
date: Thu, 03 Feb 2022 06:41:41 GMT
content-type: application/json; charset=utf-8
content-length: 1213
Body
{
"versions":[
{
"version":"08D9E6A3F2188CFF.0000000001.08D9E6A3F2188CFF.01",
"deleted":false,
"contentLength":3,
"createdTime":"2022-02-02T23:30:06.5388799Z",
"objectCreatedTime":"2022-02-02T23:30:06.5388799Z"
},
{
"version":"08D9E6A3F2188CFF.0000000002.08D9E6DCC8E6F8B3.01",
"deleted":false,
"contentLength":3,
"createdTime":"2022-02-03T06:16:58.7409587Z",
"objectCreatedTime":"2022-02-02T23:30:06.5388799Z"
},
{
"version":"08D9E6A3F2188CFF.0000000003.08D9E6DE485A7680.01",
"deleted":false,
"contentLength":3,
"createdTime":"2022-02-03T06:27:42.065216Z",
"objectCreatedTime":"2022-02-02T23:30:06.5388799Z"
},
{
"version":"08D9E6A3F2188CFF.0000000004.08D9E6DE85DA6B9F.01",
"deleted":false,
"contentLength":3,
"createdTime":"2022-02-03T06:29:25.2448159Z",
"objectCreatedTime":"2022-02-02T23:30:06.5388799Z"
},
{
"version":"08D9E6A3F2188CFF.0000000005.08D9E6DF5C82B1B3.01",
"deleted":false,
"contentLength":3,
"createdTime":"2022-02-03T06:35:25.3800371Z",
"objectCreatedTime":"2022-02-02T23:30:06.5388799Z"
}
],
"nextPageCursor":"eyJ2ZXJzaW9uIjoxLCJjdXJzb3IiOiJleUpwYm14cGJtVldaWEp6YVc5dUlqcG1ZV3h6WlN3aWJHRnpkRlpsY25OcGIyNGlPaUl3T0VRNVJUWkJNMFl5TVRnNFEwWkdMakF3TURBd01EQXdNRFV1TURoRU9VVTJSRVkxUXpneVFqRkNNeTR3TVNJc0lrTjFjbk52Y2xabGNuTnBiMjRpT2pGOSJ9"
}
Raw (unformatted)
{"versions":[{"version":"08D9E6A3F2188CFF.0000000001.08D9E6A3F2188CFF.01","deleted":false,"contentLength":3,"createdTime":"2022-02-02T23:30:06.5388799Z","objectCreatedTime":"2022-02-02T23:30:06.5388799Z"},{"version":"08D9E6A3F2188CFF.0000000002.08D9E6DCC8E6F8B3.01","deleted":false,"contentLength":3,"createdTime":"2022-02-03T06:16:58.7409587Z","objectCreatedTime":"2022-02-02T23:30:06.5388799Z"},{"version":"08D9E6A3F2188CFF.0000000003.08D9E6DE485A7680.01","deleted":false,"contentLength":3,"createdTime":"2022-02-03T06:27:42.065216Z","objectCreatedTime":"2022-02-02T23:30:06.5388799Z"},{"version":"08D9E6A3F2188CFF.0000000004.08D9E6DE85DA6B9F.01","deleted":false,"contentLength":3,"createdTime":"2022-02-03T06:29:25.2448159Z","objectCreatedTime":"2022-02-02T23:30:06.5388799Z"},{"version":"08D9E6A3F2188CFF.0000000005.08D9E6DF5C82B1B3.01","deleted":false,"contentLength":3,"createdTime":"2022-02-03T06:35:25.3800371Z","objectCreatedTime":"2022-02-02T23:30:06.5388799Z"}],"nextPageCursor":"eyJ2ZXJzaW9uIjoxLCJjdXJzb3IiOiJleUpwYm14cGJtVldaWEp6YVc5dUlqcG1ZV3h6WlN3aWJHRnpkRlpsY25OcGIyNGlPaUl3T0VRNVJUWkJNMFl5TVRnNFEwWkdMakF3TURBd01EQXdNRFV1TURoRU9VVTJSRVkxUXpneVFqRkNNeTR3TVNJc0lrTjFjbk52Y2xabGNuTnBiMjRpT2pGOSJ9"}
Get Entry Version
Endpoint | GET /standard-datastores/datastore/entries/entry/versions/version |
Description | Returns the value and metadata of a specific version of an entry. |
API key permission | Read Version |
Query Params |
|
Response |
Same as the response for Get Entry, OR204 No Content for tombstone versions of deleted entries, which contain the following headers:
|
Lua Equivalent |
DataStore:GetVersionAsync
|
Example Help
Expand
curl \
--include \
--location \
--request GET \
"https://apis.roblox.com/datastores/v1/universes/3310576216/standard-datastores/datastore/entries/entry/versions/version" \
--header "x-api-key: ${API_KEY}" \
--get \
-d "datastoreName=Coins" \
-d "entryKey=269323" \
-d "versionId=08D9E6A3F2188CFF.0000000003.08D9E6DE485A7680.01"
Response
HTTP/2 200
date: Thu, 03 Feb 2022 06:43:13 GMT
content-type: application/json
content-length: 3
content-md5: sTf90fedVsft8zZf6nUg8g==
roblox-entry-version: 08D9E6A3F2188CFF.0000000003.08D9E6DE485A7680.01
roblox-entry-created-time: 2022-02-02T23:30:06.5388799Z
roblox-entry-version-created-time: 2022-02-03T06:27:42.0652160Z
roblox-entry-attributes: {}
roblox-entry-userids: [269323]
Body
750
Examples Help
Each endpoint includes an example invocation of cURL using the Bash shell (bash
). Some response headers have been omitted for brevity, and certain JSON response bodies have been formatted for readability (the raw, unformatted response is included). This makes the content-length
and content-md5
headers inaccurate.
Where appropriate, examples use the following sample data directly:
-
Universe ID:
3310576216
-
Data Store Name:
Coins
-
Data Store Scope:
global
(default) -
Key:
269323
(a user ID) whose value is a number.
However, the x-api-key
header (see Authorization) uses the API_KEY
environment variable in place of an actual API key. Before trying to run these examples yourself, you should set this variable using the read
command:
read -s -p "API Key (will be hidden): " API_KEY
This allows you to paste the API key into your shell without it appearing in command history. When you are done trying out examples, you should regenerate the key before using it in production code.
Error Codes
Data store API error responses include the high-level cause of the error applicable to all Open Cloud API endpoints, the error details specific to the data store, and an error message to help you understand the error. You can use Open Cloud error code, the value of the “error” field, and “datastoreErrorCode” in the “errorDetails” field to handle errors.
For example, the following payload displays an error response with the high-level Open Cloud cause of the error (“INVALID_ARGUMENT”), the explanatory error message(“Content-md5 header required.”), and the data store error code (“ContentMd5Required”).
{
"error": "INVALID_ARGUMENT",
"message": "Content-md5 header required.",
"errorDetails": [
{
"errorDetailType" : "DatastoreErrorInfo",
"datastoreErrorCode" : "ContentMd5Required"
}
]
}
Reference the following table for a summary of all Open Cloud and data store errors:
HttpStatus | Open Cloud Error | Data Store Error | Message |
---|---|---|---|
400 | INVALID_ARGUMENT | ContentLengthRequired | Content-Length header required. |
400 | INVALID_ARGUMENT | InvalidUniverseId | Invalid universe id. |
400 | INVALID_ARGUMENT | InvalidCursor | Invalid cursor. |
400 | INVALID_ARGUMENT | InvalidVersionId | Invalid version id. |
400 | INVALID_ARGUMENT | ExistingValueNotNumeric | The existing value is not numeric. |
400 | INVALID_ARGUMENT | IncrementValueTooLarge | The numeric value is too large to increment. |
400 | INVALID_ARGUMENT | IncrementValueTooSmall | The numeric value is too small to increment. |
400 | INVALID_ARGUMENT | InvalidDataStoreScope | Invalid datastore scope. |
400 | INVALID_ARGUMENT | InvalidEntryKey | Invalid entry key. |
400 | INVALID_ARGUMENT | InvalidDataStoreName | Invalid datastore name. |
400 | INVALID_ARGUMENT | InvalidStartTime | Invalid start time. |
400 | INVALID_ARGUMENT | InvalidEndTime | Invalid end time. |
400 | INVALID_ARGUMENT | InvalidAttributes | Invalid attributes. |
400 | INVALID_ARGUMENT | InvalidUserIds | Invalid userids. |
400 | INVALID_ARGUMENT | ContentMd5Required | Content-md5 header required. |
400 | INVALID_ARGUMENT | InvalidLimit | Invalid limit. |
400 | INVALID_ARGUMENT | ExclusiveCreateAndMatchVersionCannotBeSet | Only one of exclusive create or matchVersion parameter can be provided. |
400 | INVALID_ARGUMENT | ContentTooBig | Content too big. |
400 | INVALID_ARGUMENT | ChecksumMismatch | Md5 checksum mismatch. |
400 | INVALID_ARGUMENT | ContentNotJson | Content is not valid json string. |
400 | INVALID_ARGUMENT | InvalidSortOrder | Invalid sort order. |
403 | INSUFFICIENT_SCOPE | Forbidden | The api key does not have sufficient scope to perform this operation. |
403 | INSUFFICIENT_SCOPE | InsufficientScope | Insufficient scope to perform the operation. |
404 | NOT_FOUND | DatastoreNotFound | Datastore not found. |
404 | NOT_FOUND | EntryNotFound | Entry not found in the datastore. |
404 | NOT_FOUND | VersionNotFound | Version is not found |
429 | RESOURCE_EXHAUSTED | TooManyRequests | Too many requests. |
500 | INTERNAL | Unknown | Error unknown. |