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.

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.

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

See REST API reference for the list of supported endpoints.


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

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, /v1/sql✅ Yes
graphql/graphql✅ Yes
❌ No

Exception: /livez and /readyz endpoints don't belong to any scope. Access to these endpoints can't be controlled using API scopes.

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;


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: "",



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.

Example request

curl -H "Authorization: EXAMPLE-API-TOKEN"

Continue wait

If the request takes too long to be processed, Cube Backend responds with { "error": "Continue wait" } and 200 status code. This is how the long polling mechanism in Cube is implemented. Clients should continuously retry the same query in a loop until they get a successful result. Subsequent calls to the Cube endpoints are idempotent and don't lead to scheduling new database queries if not required by the refresh_key. Also, receiving Continue wait doesn't mean the database query has been canceled, and it's actually still being processed by the Cube. Database queries that weren't started and are no longer waited by the client's long polling loop will be marked as orphaned and removed from the querying queue.

Possible reasons of Continue wait:

  • The query requested is heavy, and it takes some time for the database to process it. Clients should wait for its completion, continuously sending the same REST API request. continueWaitTimeout can be adjusted in order to change the time Cube waits before returning Continue wait message.
  • There are many queries requested and Cube backend queues them to save database from overloading.

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.


Cube supports paginated requests for the /v1/load endpoint by including limit and offset parameters in the query. For example, the following query will retrieve rows 101-200 from the Orders cube:

  "dimensions": ["Orders.status"],
  "measures": ["Orders.count"],
  "timeDimensions": [
      "dimension": "Orders.createdAt",
      "dateRange": "last year",
      "granularity": "day"
  "limit": 100,
  "offset": 100