Public API
The Keebo public API provides programmatic access to Warehouse Optimization for Snowflake (KWO). It is a REST API secured with OAuth2 and hosted at:
https://kwo.api.keebo.ai/v1
The API covers Snowflake accounts, warehouses, and warehouse optimization settings. All requests require a bearer token obtained through the OAuth2 client credentials flow — see How Is API Authentication Configured? below.
How Is API Authentication Configured?
Before using the API, authenticate with the OAuth2 service to obtain an access token.
Run the following script:
# Set the client credentials provided by Keebo
export CLIENT_ID="your_client_id"
export CLIENT_SECRET="your_client_secret"
# Store the access token in an environment variable
export ACCESS_TOKEN=$(curl -s -X POST https://auth.keebo.ai/oauth/token \
-H "Content-Type: application/json" \
-d '{
"client_id": "'$CLIENT_ID'",
"client_secret": "'$CLIENT_SECRET'",
"audience": "https://kwo.api.keebo.ai/v1",
"grant_type": "client_credentials"
}' | jq -r '.access_token')
# Verify the token is set
echo $ACCESS_TOKEN
- CLIENT_ID — Found on the General Settings page in the Keebo portal under "Public API."
- CLIENT_SECRET — Found on the General Settings page in the Keebo portal under "Public API."
- ACCESS_TOKEN — The output of the script. Pass this token as a
Bearertoken in theAuthorizationheader of all API requests.
What Can Be Done With the API?
Accounts
Warehouses
- List warehouses
- Add a warehouse
- Get warehouse details
- Update a warehouse
- Enable cost savings for a warehouse
- Disable cost savings for a warehouse
Warehouse default optimization settings
Account Endpoints
How Is a Snowflake Account Added?
POST /accounts
Enrolls a Snowflake account in Keebo.
Request body (application/json):
| Field | Type | Required | Description |
|---|---|---|---|
host | string | Yes | Snowflake host URL (e.g. xy12345.us-east-2.aws.snowflakecomputing.com) |
user | string | Yes | Snowflake user (e.g. KEEBO_USER) |
role | string | Yes | Snowflake role (e.g. KEEBO_ROLE) |
database | string | Yes | Keebo metadata database (e.g. KEEBO_DATABASE) |
schema | string | Yes | Keebo metadata schema (e.g. KEEBO_SCHEMA) |
credentials.keyPair.privateKey | string | Yes | RSA private key in PEM format, base64-encoded |
credentials.keyPair.privateKeyPassword | string | No | Password for the private key, if encrypted |
Responses:
| Status | Meaning |
|---|---|
201 | Account enrolled. Returns snowflakeAccountIdentifier. |
400 | Bad request — invalid host, missing privileges, invalid role, or invalid credentials. |
401 | Unauthorized. |
409 | Account already enrolled. |
500 | Internal server error. |
Snowflake account identifier
The snowflakeAccountIdentifier value returned on success is used as the path parameter for all warehouse and rules endpoints. Retrieve it from Snowflake at any time by running:
SELECT CURRENT_ORGANIZATION_NAME() || '-' || CURRENT_ACCOUNT_NAME();
Warehouse Endpoints
All warehouse endpoints are scoped to a Snowflake account via the snowflakeAccountIdentifier path parameter.
How Are Warehouses Listed?
GET /accounts/{snowflakeAccountIdentifier}/warehouses
Returns a paginated list of warehouses registered with Keebo for the specified account.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
snowflakeAccountIdentifier | string | Snowflake account identifier (e.g. acme-marketing_test_account) |
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
pageSize | integer | 50 | Number of results per page (1–100) |
pageNumber | integer | 0 | Zero-based page index |
Responses:
| Status | Meaning |
|---|---|
200 | Returns an array of warehouse detail objects. |
400 | Bad request — unknown account. |
401 | Unauthorized. |
500 | Internal server error. |
How Is a Warehouse Added?
POST /accounts/{snowflakeAccountIdentifier}/warehouses
Registers a Snowflake warehouse with Keebo for the specified account.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
snowflakeAccountIdentifier | string | Snowflake account identifier |
Request body (application/json):
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Warehouse name. If the name contains special characters, it must be quoted (e.g. "\"my_$pec1al warehouse\"") |
labels | string[] | No | Labels to tag the warehouse (e.g. ["looker", "finance"]). Defaults to [] |
costSavingsConfig.aggressiveness | string | No | Cost savings aggressiveness level. Defaults to BALANCED. See Cost Savings Aggressiveness |
costSavingsConfig.enabled | boolean | No | Whether cost savings is enabled. Defaults to true |
Responses:
| Status | Meaning |
|---|---|
200 | Warehouse registered. Returns a warehouse detail object. |
400 | Bad request — missing permissions or warehouse limit reached. |
401 | Unauthorized. |
409 | Warehouse already registered. |
500 | Internal server error. |
How Are Warehouse Details Retrieved?
GET /accounts/{snowflakeAccountIdentifier}/warehouses/{warehouseName}
Returns details for a specific warehouse.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
snowflakeAccountIdentifier | string | Snowflake account identifier |
warehouseName | string | Warehouse name |
Responses:
| Status | Meaning |
|---|---|
200 | Returns a warehouse detail object. |
400 | Bad request — unknown account or warehouse not found. |
401 | Unauthorized. |
500 | Internal server error. |
How Is a Warehouse Updated?
PATCH /accounts/{snowflakeAccountIdentifier}/warehouses/{warehouseName}
Updates one or more warehouse settings. Partial updates are supported using the updateMask field — only fields listed in updateMask are applied; all others are ignored. If updateMask is omitted, all fields are required and a full update is performed.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
snowflakeAccountIdentifier | string | Snowflake account identifier |
warehouseName | string | Warehouse name |
Request body (application/json):
| Field | Type | Description |
|---|---|---|
warehouse.name | string | New warehouse name |
warehouse.size | string | Warehouse size (e.g. X-SMALL, MEDIUM) |
warehouse.minClusterCount | integer | Minimum number of clusters |
warehouse.maxClusterCount | integer | Maximum number of clusters |
warehouse.autoSuspend | integer | Seconds of inactivity before auto-suspend |
warehouse.changeDetectionType | string | How to handle size changes detected outside Keebo: ACCEPT_CHANGE_AS_DEFAULT or GENERATE_ERROR |
warehouse.costSavingsConfig.aggressiveness | string | Cost savings aggressiveness level |
updateMask | string | Comma-separated list of fields to update (e.g. "size,minClusterCount,maxClusterCount") |
Example — update size only:
{
"warehouse": {
"size": "X-SMALL"
},
"updateMask": "size"
}
Example — update cluster counts:
{
"warehouse": {
"minClusterCount": 3,
"maxClusterCount": 7
},
"updateMask": "minClusterCount,maxClusterCount"
}
Example — rename a warehouse:
{
"warehouse": {
"name": "new_warehouse_name"
},
"updateMask": "name"
}
Responses:
| Status | Meaning |
|---|---|
200 | Warehouse updated. Returns a warehouse detail object. |
400 | Bad request. |
401 | Unauthorized. |
409 | Conflict. |
500 | Internal server error. |
How Is Cost Savings Enabled for a Warehouse?
POST /accounts/{snowflakeAccountIdentifier}/warehouses/{warehouseName}:enableCostSavings
Enables automated cost savings for a specific warehouse.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
snowflakeAccountIdentifier | string | Snowflake account identifier |
warehouseName | string | Warehouse name |
Responses:
| Status | Meaning |
|---|---|
200 | Cost savings enabled. No response body. |
400 | Bad request — missing permissions, unknown account, auto-resume disabled, or active errors present. |
401 | Unauthorized. |
500 | Internal server error. |
How Is Cost Savings Disabled for a Warehouse?
POST /accounts/{snowflakeAccountIdentifier}/warehouses/{warehouseName}:disableCostSavings
Disables automated cost savings for a specific warehouse.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
snowflakeAccountIdentifier | string | Snowflake account identifier |
warehouseName | string | Warehouse name |
Responses:
| Status | Meaning |
|---|---|
200 | Cost savings disabled. No response body. |
400 | Bad request — missing permissions, unknown account, or active errors present. |
401 | Unauthorized. |
500 | Internal server error. If cost savings was successfully disabled but the warehouse could not be restored to its original settings, a DATA_LOSS response is returned with a RESTORE_SETTINGS_FAILURE reason and the expected settings values in the error details. |
Warehouse Default Optimization Settings
Each warehouse has a set of default optimization settings that apply continuously across all hours and days. These settings serve as the baseline behavior. Time-specific rules — configured separately in the Keebo portal — override the defaults for the periods they cover, then revert to these defaults outside of those windows.
How Are Default Warehouse Optimization Settings Updated?
PATCH /accounts/{snowflakeAccountIdentifier}/warehouses/{warehouseName}/rules/default
Updates the default optimization settings for a warehouse. Partial updates are supported using updateMask — only the fields listed in updateMask are applied.
Path parameters:
| Parameter | Type | Description |
|---|---|---|
snowflakeAccountIdentifier | string | Snowflake account identifier |
warehouseName | string | Warehouse name |
Request body (application/json):
| Field | Type | Description |
|---|---|---|
rule.downsizing.enabled | boolean | Enable or disable automated downsizing |
rule.multiCluster.enabled | boolean | Enable or disable multi-cluster optimization |
rule.multiCluster.lowestMaxClusterCount | integer | Floor for the maximum cluster count when multi-cluster optimization is active |
updateMask | string | Comma-separated list of fields to update (e.g. "downsizing,multiCluster") |
Example:
{
"rule": {
"downsizing": { "enabled": true },
"multiCluster": { "enabled": true, "lowestMaxClusterCount": 2 }
},
"updateMask": "downsizing,multiCluster"
}
Responses:
| Status | Meaning |
|---|---|
200 | Settings updated. Returns the full default rule object. |
400 | Bad request — unknown account, unknown warehouse, or unsupported feature (downsizing or multi-cluster not available for this warehouse). |
500 | Internal server error. |
Response body — the returned rule object:
| Field | Type | Description |
|---|---|---|
name | string | Rule identifier. Always "default" for this endpoint. |
createTime | string | ISO 8601 timestamp of when the rule was created. |
defaultSize | string | The warehouse size this rule defaults to. See Warehouse Sizes. |
defaultClusterCount.min | integer | Minimum cluster count. |
defaultClusterCount.max | integer | Maximum cluster count. |
schedule.days | string[] | Days the rule applies. The default rule covers all seven days. |
schedule.start | object | Time of day the rule starts. Empty object represents midnight (00:00:00). |
schedule.duration | string | Duration the rule applies, in seconds with s suffix (e.g. "86400s" for the full day). |
downsizing.enabled | boolean | Whether automated downsizing is active. |
multiCluster.enabled | boolean | Whether multi-cluster optimization is active. |
multiCluster.lowestMaxClusterCount | integer | Floor for the maximum cluster count when multi-cluster optimization is active. |
Reference
Cost Savings Aggressiveness
The aggressiveness field controls how aggressively Keebo optimizes cost versus performance.
| Value | Description |
|---|---|
BEST_PERFORMANCE | Prioritizes query performance over cost savings |
GOOD_PERFORMANCE | Favors performance with moderate savings |
BALANCED | Default. Balances cost savings and performance equally |
LOW_COST | Favors cost savings with acceptable performance impact |
LOWEST_COST | Maximizes cost savings |
Warehouse Details Object
Warehouse list, add, get, and update endpoints return a warehouse details object with the following fields:
| Field | Type | Description |
|---|---|---|
name | string | Warehouse name. |
size | string | Warehouse size. See Warehouse Sizes. |
minClusterCount | integer | Minimum number of clusters. |
maxClusterCount | integer | Maximum number of clusters. |
autoSuspend | integer | Seconds of inactivity before the warehouse auto-suspends. |
changeDetectionType | string | How size changes detected outside Keebo are handled: ACCEPT_CHANGE_AS_DEFAULT or GENERATE_ERROR. |
costSavingsConfig.aggressiveness | string | Cost savings aggressiveness level. See Cost Savings Aggressiveness. |
costSavingsConfig.enabled | boolean | Whether cost savings is enabled for this warehouse. |
Warehouse Sizes
Valid values for the size field:
X-Small, Small, Medium, Large, X-Large, 2X-Large, 3X-Large, 4X-Large, 5X-Large, 6X-Large
Error Response Format
All 4xx and 5xx responses return a JSON error object:
{
"code": 3,
"grpc-code": "INVALID_ARGUMENT",
"message": "Human-readable error description",
"details": [
{
"reason": "REASON_CODE",
"metadata": {
"message": "Additional detail",
"invalidValue": "the value that caused the error"
}
}
]
}