Skip to main content

API overview

Programmatically list and run Hex projects with the API.

info

Admin APIs are available on both the Team and Enterprise plans; certain endpoints are exclusive to the Enterprise plan.

The Hex public API allows teams to programmatically interact with their Hex workspace — including listing projects, triggering runs of published projects, managing access controls, and configuring data connections.

  • Core Admin APIs, such as user, group, collection, and project run management, are available to both Team and Enterprise plans.
  • Observability API for advanced capabilities - such as retrieving queried tables for a given project (eg. GetQueriedTables) - is available on the Enterprise plan only.

If you are looking to integrate Hex project runs into orchestration tools, check our native integrations with Airflow, Dagster, and Orchestra.

For full path parameters and request/response schemas, view the API reference.

Authentication

API requests are authenticated using OAuth 2.0 Bearer Tokens in the header of the HTTP request. The token is always bound to a single Hex user's account and requests are executed as that Hex user, meaning the user can only execute requests against projects that are in line with the current permissions for that project.

Token creation

There are two types of tokens users can create: Personal access tokens and Workspace tokens. Tokens are prefixed to indicate their type: hxtp_ for personal access tokens and hxtw_ for workspace tokens. This prefix enables security scanning tools to detect accidentally exposed tokens.

UI showing API keys page

Personal access tokens

Personal access tokens mirror the same permissions that a user has within the Hex product. They can be created by anyone with an Editor or higher workspace role. Unlike workspace tokens (which can have no expiration), they must be configured to expire after a fixed duration.

info

Enforcing token expiration for personal access tokens ensures that tokens are rotated frequently.

To create a Personal access token, head to the user settings, and then the API keys page under the Account section. Then, select the New Token button and provide a description and an expiration time frame. An existing token can be regenerated at any time by selecting the three-dot menu to the right of the token, and selecting Regenerate.

warning

Regenerating a personal access token will generate a new value for the token and immediately revoke the existing token.

If a user is deactivated in a workspace, their personal access tokens will no longer work.

Workspace tokens

Workspace tokens are created, managed, and shared by Admins of a workspace. Unlike personal access tokens, workspace tokens can be configured to never expire. This more permissive setting is available for workspace tokens, since any Admin can revoke this token at any time.

To create a Workspace token, head to the user settings, and then the API keys page under the Account section. Then, select the New Token button and provide a description and an expiration time frame. Workspace tokens can be configured to have the following scopes:

  • Read projects: The token will work with any API endpoint that only gets information (e.g. ListProjects and GetProjectRuns).
  • Run projects: The token will also work with the RunProjects endpoint.
  • For Users, Groups, Collections, and Data connections: The token can be specified to have read-only or write access (which includes read).
Example dialog of adding scopes to an API token
tip

If you are creating a token that is used to orchestrate projects across a workspace, consider using a Workspace token so that the token is not scoped to an individual user.

Comparison

FeaturePersonal access tokenWorkspace token
Required workspace roleEditor or higherAdmin only
Maximum expirationFollows expiration rules configured by AdminsCan be configured to never expire
PermissionsMirrors an individual user's permissionsMirrors the admin permissions for the workspace, with additional configuration for scopes

Token expiration

When creating a token, users can specify an expiration period:

  • Personal access tokens: When creating a personal access token, users can specify a time to live that is equal to, or less than, the maximum expiration time configured by an Admin (see below). Durations may include 7, 30, 60, 90, or 120 days.
  • Workspace tokens: When creating a workspace token, admins can specify an expiration that is a fixed duration (one of 7, 30, 60, 90, or 120 days), or no expiry.

To configure the maximum expiration for a personal access token, Admins can head to the Integrations page, under the Workspace section of Settings.

Users will receive an email notification 72 hours before and 24 hours before any token expires, warning them that the token will be expiring soon. Tokens can be manually revoked by clicking the three-dot menu to the right of the token, and selecting Revoke. Once a token is revoked or expired, the token can never again be used to authenticate requests.

Using the API

The Hex API provides programmatic access to a broad set of capabilities within the Hex platform, supporting both project execution and administrative workflows.

Initially designed to run published Hex projects with specific inputs—enabling automation of workflows, refreshing cached query results, and updating app state—the API has since expanded to include endpoints for managing:

  • Users and their roles
  • Groups and access controls
  • Collections of projects
  • Data connections and credentials (for Tier 1 connectors)
  • Threads and their metadata

This expanded API surface allows teams to automate administration, enforce governance, and integrate Hex more deeply into existing systems. You can find the full reference documentation for the Hex API here.

info

If your workspace is using Directory Sync, users and groups will continue to be managed there and not via API.

The examples below use the requests package to run the API, though it can also be accessed using tools like running a cURL command, Postman, or any HTTP client of your choice.

Setup needed for the API

To use the API correctly, first run some setup code. Ensure that you replace values for your specific use-case.

  • Base URL: For most Hex users, this will be https://app.hex.tech/api/v1. For single tenant, EU multi tenant, and HIPAA multi tenant customers, replace app.hex.tech with your custom URL (e.g. atreides.hex.tech, eu.hex.tech).
  • Project ID: The project ID can be found by visiting the project you wish to run and using the "Copy project id" option in either the help menu or 3-dot menu at top right, in the notebook and published app respectively. More on how you can retrieve a project's ID is described here. Additionally, the project ID can be found in the Variables view of the sidebar.
  • Token: See the above section on token creation. Consider using a secret to store this more securely.
import requests

# Single tenant, HIPAA, and EU users will need to replace this with their Hex URL
BASE_URL = 'https://app.hex.tech/api/v1'

# Replace this with the project ID
PROJECT_ID = '5a8591dd-4039-49df-9202-96385ba3eff8'

# Replace this with your token (format: hxtp_<96 hex chars> for personal, hxtw_<96 hex chars> for workspace)
TOKEN = 'hxtp_5bbf1c8b1989d6657d5c...'

Run a published project with default inputs

This API call uses the RunProject endpoint to run a published project with its default inputs. It does not update the cache for this project.

response = requests.post(
url=f"{BASE_URL}/projects/{PROJECT_ID}/runs",
headers={"Authorization" : f"Bearer {TOKEN}"}
)

The response from this request will contain a runUrl which will display the results of the project run as a Snapshot, viewable by any user who has at least view access to the project.

Run a published project with custom inputs

The RunProject endpoint contains an optional inputParams body parameter that allows users to specify the values for Input parameters to be used in the project run.

inputs = {
"inputParams": {
"user_name": "j_doe",
"team": "finance",
"id": 1234567890
}
}

response = requests.post(
url=f"{BASE_URL}/projects/{PROJECT_ID}/runs",
json=inputs,
headers={"Authorization" : f"Bearer {TOKEN}"}
)

Update the cached state and query cache of a published project

The RunProject API allows control over two key caching options: updatePublishedResults for updating the published app's state, and useCachedSqlResults for controlling whether cached SQL results are used.

updatePublishedResults: When updatePublishedResults is set to false in a RunProject request (the default), the project run will not update the published app state. When updatePublishedResults is set to true, the project run will update the cached state of the published app with the latest run results⁠. This ensures that viewers see the results generated by the run when they open the app.⁠

In order to set updatePublishedResults to true, "Show results from a publish, or scheduled run" must be enabled. If inputParams are included in the request with updatePublishedResults set to true, the provided parameter values will be ignored, as updating the published app’s cache state requires the default Input parameter values to be used.

useCachedSqlResults: When useCachedSqlResults is set to true in a RunProject request (the default), the project will use cached SQL results if available. When useCachedSqlResults is set to false, SQL cells will run without hitting the cache, essentially refreshing the cached SQL query results for future runs.

In order for a query to execute and update cached results using useCachedSqlResults, "Use SQL caching in published app" must be enabled on the project’s Published app run settings.

# Forces a fresh run of SQL queries and updates the published app with new results
inputs = {
"useCachedSqlResults": "false",
"updatePublishedResults": "true"
}

response = requests.post(
url=f"{BASE_URL}/projects/{PROJECT_ID}/runs",
json=inputs,
headers={"Authorization" : f"Bearer {TOKEN}"}
)

Create a new group given a list of user emails

warning

Group names are not unique - if you create a group with the same name as one that already exists, a new group with the same name will be created.

Step 1: Get User IDs from Emails

import requests

# Example: Fetch user list to get IDs
resp = requests.get(
url=f"{BASE_URL}/users",
headers={"Authorization": f"Bearer {YOUR_API_TOKEN}"}
)
users = resp.json()["users"]
email_to_id = {u["email"]: u["id"] for u in users}
tip

You’ll need to fetch all users across paginated API responses

Step 2: Create the Group

group_payload = {
"name": "Data Engineering Team",
"members": {
"users": [{"id": email_to_id["alice@example.com"]}, {"id": email_to_id["bob@example.com"]}]
}
}

resp = requests.post(
url=f"{BASE_URL}/groups",
json=group_payload,
headers={"Authorization": f"Bearer {YOUR_API_TOKEN}"}
)

Create a new Collection with specific sharing permissions

The CreateCollection endpoint lets you define sharing settings for users, groups, or the workspace at creation.

info

You must have admin privileges to set group and workspace-level access.

collection_payload = {
"name": "Q3 Projects",
"description": "All Q3 cross-functional initiatives",
"members": {
"groups": [
{"id": "group-analytics", "access": "MEMBER"},
{"id": "group-admins", "access": "MANAGER"}
],
"workspace": {"members": "MEMBER"}
}
}

resp = requests.post(
url=f"{BASE_URL}/collections",
json=collection_payload,
headers={"Authorization": f"Bearer {YOUR_API_TOKEN}"}
)

Change Collection permissions

To update permissions on an existing Collection, use the EditCollection endpoint. You can upsert user/group/workspace access using the sharing.upsert field.

edit_payload = {
"collectionId": "COLLECTION_ID",
"sharing": {
"upsert": {
"groups": [
{"id": "group-analytics", "access": "NONE"}, # Remove access
{"id": "group-ops", "access": "MEMBER"} # Add new group
],
"workspace": {"members": "MANAGER"}
}
}
}

resp = requests.patch(
url=f"{BASE_URL}/collections/{COLLECTION_ID}",
json=edit_payload,
headers={"Authorization": f"Bearer {YOUR_API_TOKEN}"}
)
print(resp.json())

Rotate data connection credentials

To rotate secrets (e.g., passwords, service accounts), use the EditDataConnection endpoint and supply a new connectionDetails block.

info

This is only applicable for Tier 1 data connectors.

Step 1: Get data connection ID

import requests

resp = requests.get(
url=f"{BASE_URL}/data-connections",
headers={"Authorization": f"Bearer {YOUR_API_TOKEN}"}
)

# Print available connections
for conn in resp.json()["values"]:
print(f"{conn['name']} → ID: {conn['id']}")

Step 2: Update the connectionDetails block

edit_conn_payload = {
"connectionDetails": {
"snowflake": {
"accountName": "test123.us-east-2.com",
"warehouse": "FOO",
"database": "MY_DB",
"schema": "MY_SCHEMA",
"username": "hex_user",
"privateKey": "NEW_PRIVATE_KEY",
"passphrase": "NEW_PASSPHRASE",
"role": "MY_ROLE"
}
}
}

resp = requests.patch(
url=f"{BASE_URL}/data-connections/{DATA_CONNECTION_ID}",
json=edit_conn_payload,
headers={"Authorization": f"Bearer {YOUR_API_TOKEN}"}
)