Documentation
REST API

REST API

REST API enables Cube to deliver data over the HTTP protocol to certain kinds of data applications, including but not limited to the following ones:

Often, the REST API is used to enable embedded analytics (opens in a new tab) and real-time analytics (opens in a new tab) use cases.

See REST API reference for the list of supported API endpoinsts. Also, check query format for details about query syntax.

You can find a mostly complete OpenAPI documentation for the REST API in the linked openspec.yml file (opens in a new tab) that you can use with tools like Swagger.

If you've chosen GraphQL (opens in a new tab) as a query language for your front-end application, consider using the GraphQL API that Cube also provides.

REST API also provides endpoints for GraphQL API and Orchestration API. However, they target specific use cases and are not considered part of the REST API.

Transport

The REST API supports the following transports:

TransportDescriptionWhen to use
HTTPHTTP-based protocol with long pollingUse by default
WebSocketWebSocket (opens in a new tab)-based protocol with support for real-time updatesUse for applications that require subscriptions to real-time updates

HTTP transport

You can use the curl utility to execute requests to the REST API:

curl \
  -H "Authorization: JSON.WEB.TOKEN" \
  -G \
  --data-urlencode 'query={
    "dimensions": [
      "users.state",
      "users.city",
      "orders.status"
    ],
    "measures": [
      "orders.count"
    ],
    "filters": [
      {
        "member": "users.state",
        "operator": "notEquals",
        "values": ["us-wa"]
      }
    ],
    "timeDimensions": [
      {
        "dimension": "orders.created_at",
        "dateRange": ["2020-01-01", "2021-01-01"]
      }
    ],
    "limit": 10
  }' \
  http://localhost:4000/cubejs-api/v1/load

You can also use the jq utility (opens in a new tab) to format responses from the REST API in your terminal:

curl \
  -H "Authorization: JSON.WEB.TOKEN" \
  -G \
  --data-urlencode 'query={"measures": ["orders.count"]}' \
  http://localhost:4000/cubejs-api/v1/load | jq .data

You can also introspect the data model by querying the /v1/meta endpoint:

curl \
  -H "Authorization: JSON.WEB.TOKEN" \
  http://localhost:4000/cubejs-api/v1/meta | jq

You can also use the JavaScript SDK to call the REST API from your JavaScript applications.

WebSocket transport

WebSocket can be used to provide real-time experience. You can configire it via the CUBEJS_WEB_SOCKETS environment variable.

Clients using the JavaScript SDK can be switched to WebSocket by passing WebSocketTransport to the CubeApi constructor:

import cube from "@cubejs-client/core"
import WebSocketTransport from "@cubejs-client/ws-transport"
 
const cubeApi = cube({
  transport: new WebSocketTransport({
    authorization: CUBE_TOKEN,
    apiUrl: "ws://localhost:4000/"
  })
})

See this recipe for an example of real-time data fetch using the WebSocket transport.

Configuration

REST API is enabled by default and secured using API scopes and CORS.

To find your REST API endpoint in Cube Cloud, go to the Overview page, click API credentials, and choose the REST API tab.

Base path

By default, all REST API endpoints are prefixed with a base path of /cubejs-api, e.g., the /v1/load endpoint will be available at /cubejs-api/v1/load.

Exception: /livez and /readyz endpoints are not prefixed with a base path.

You can set a desired base path using the basePath configuration option.

API scopes

Each REST API endpoint belongs to an API scope, e.g., the /v1/load endpoint belongs to the data scope. API scopes allow to secure access to API endpoints by making them accessible to specific users only or disallowing access for everyone. By default, API endpoints in all scopes, except for jobs, are accessible for everyone.

API scopeREST API endpointsAccessible by default?
meta/v1/meta✅ Yes
data/v1/load✅ Yes
graphql/graphql✅ Yes
sql/v1/sql✅ Yes
jobs/v1/pre-aggregations/jobs❌ No
No scope/livez, /readyz✅ Yes

You can set accessible API scopes for all requests using the CUBEJS_DEFAULT_API_SCOPES environment variable. For example, to disallow access to the GraphQL API for everyone, set CUBEJS_DEFAULT_API_SCOPES to meta,data.

You can also select accessible API scopes for each request using the contextToApiScopes configuration option, based on the provided security context. For example, to restrict access to the /v1/meta endpoint to service accounts only, you can set CUBEJS_DEFAULT_API_SCOPES to data,graphql and use the following configuration in the cube.js file, assuming that service accounts have service: true in their security context:

module.exports = {
  contextToApiScopes: (securityContext, defaultScopes) => {
    if (securityContext.service) {
      return ["meta", ...defaultScopes];
    }
 
    return defaultScopes;
  },
};

CORS

REST API supports Cross-Origin Resource Sharing (CORS) (opens in a new tab). By default, requests from any origin (*) are allowed.

You can configure CORS using the http.cors configuration option. For example, to allow requests from a specific domain only, use the following configuration in the cube.js file:

module.exports = {
  http: {
    cors: {
      origin: "https://example.com",
    },
  },
};

Prerequisites

Authentication

Cube uses API tokens to authorize requests and also for passing additional security context, which can be used in the queryRewrite property in your cube.js configuration file.

The API Token is passed via the Authorization Header. The token itself is a JSON Web Token (opens in a new tab), the Security section describes how to generate it.

In the development environment the token is not required for authorization, but you can still use it to pass a security context.

Error handling

Cube REST API has basic errors and HTTP Error codes for all requests.

StatusError responseDescription
400Error messageGeneral error. It may be a database error, timeout, or other issue. Check error message for details.
403Authorization header isn't setYou didn't provide an auth token. Provide a valid API Token or disable authorization.
403Invalid tokenThe auth token provided is not valid. It may be expired or have invalid signature.
500Error messageCube internal server error. Check error message for details.

Request span annotation

For monitoring tools such as Cube Cloud proper request span annotation should be provided in x-request-id header of a request. Each request id should consist of two parts: spanId and requestSequenceId which define x-request-id as whole: ${spanId}-span-${requestSequenceId}. Values of x-request-id header should be unique for each separate request. spanId should define user interaction span such us Continue wait retry cycle and it's value shouldn't change during one single interaction.

Troubleshooting

Continue wait

If the request takes too long to be processed, the REST API responds with { "error": "Continue wait" } and the status code 200.

This is part of the long polling mechanism:

  • After an API instance receives a request, it adds the query to the query queue.
  • If the query takes too long to be processed, the API instance will respond with Continue wait. Receiving Continue wait doesn't mean the database query has been canceled, and it's actually still being processed by Cube.
  • Clients who received Continue wait should continuously retry the same query in a loop until they get a successful result. Database queries that are no longer retried by clients will be marked as orphaned and removed from the query queue. Subsequent calls to the REST API are idempotent and don't lead to scheduling new database queries if not required by the refresh_key.

Possible reasons of Continue wait:

  • The query is too heavy for the upstream database, i.e., it takes too long to be processed.
  • The maximum concurrency is reached, i.e., there are many queries waiting in the query queue until the previous ones are completed.

continueWaitTimeout configuration option can be adjusted in order to change the time Cube waits before returning Continue wait message. However, it's recommended to address the root cause of the issue instead of increasing the timeout: