---
title: Data Warehouse Exports
description: "Export Statsig events, exposures, and metric data into your cloud data warehouse on a schedule for downstream BI, modeling, and custom analysis."
product: general
lang: en
token_estimate: 4987
---
# Data Warehouse Exports

> For AI agents: a documentation index is available at [/llms.txt](/llms.txt). Append `.md` to any page URL for markdown, or send `Accept: text/markdown`.

## How data warehouse exports work

You can export your data from Statsig to your data warehouse using a data connection. The data connection lets you send exposures and events directly to your warehouse for further analysis. Statsig supports connections to Snowflake, Redshift, S3, BigQuery, and Databricks.

> **Info:**
>
> Data Warehouse Exports are an Enterprise-only feature. If you're on the Developer or Pro tiers and want to upgrade to Enterprise, [contact the Statsig team](https://www.statsig.com/contact/demo) or Statsig support.

## Set up a data warehouse export

1. Go to Statsig Console.
2. Navigate to the Help and Tools Section on the side navigation bar.
3. Go to "Exports List".

![Statsig Help and Tools menu showing Exports List option](https://docs.statsig.com/images/exports-list.png)

On the Exports page, you find an _Export History_ tab and a _Scheduled_ tab. The _Export History_ tab lists all previous experimentation exports triggered from the _Experiment Results_ page. To export one-off experimentation results, refer to [Experiment Result Exports](https://docs.statsig.com/integrations/data-exports/experiment_result_exports).

Under the _Scheduled_ tab, set up a data warehouse connection. Configure the connection with the necessary credentials and grant Statsig Read, Write, and Delete permissions. The warehouse-specific tabs below show the fields Statsig requires and example SQL you can use to create each export table yourself.

![Scheduled export setup form for Snowflake connection with credential fields](https://docs.statsig.com/images/exports-schedule.png)

## Export table schema

Statsig can export both Exposures and Events to your destination. The exported columns are logically the same across SQL warehouses, but the physical types vary by warehouse. S3 is a file destination rather than a SQL table destination.

#### BigQuery

**Setup summary**

Configure:

- BigQuery Project ID
- BigQuery Dataset ID
- one destination table name per export type

Permissions:

- Grant the Statsig service account `BigQuery User` at the project level.
- Grant the same service account `BigQuery Data Editor` on the target dataset.

Statsig uses this access to create the table if needed and validate the connection by inserting and deleting a test row.

If you prefer to create the export tables yourself, you can use SQL like this:

```sql
CREATE TABLE IF NOT EXISTS `PROJECT_ID.DATASET_ID.EXPOSURES_TABLE` (
  company_id STRING,
  unit_id STRING,
  unit_type STRING,
  exposure_type STRING,
  name STRING,
  rule STRING,
  experiment_group STRING,
  first_exposure_utc TIMESTAMP,
  first_exposure_pst_date DATE,
  as_of_pst_date DATE,
  percent FLOAT64,
  rollout BIGINT,
  user_dimensions STRING,
  inserted_at TIMESTAMP,
  rule_name STRING,
  group_id STRING,
  non_analytics BOOLEAN
);
```

```sql
CREATE TABLE IF NOT EXISTS `PROJECT_ID.DATASET_ID.EVENTS_TABLE` (
  user_id STRING,
  stable_id STRING,
  custom_ids JSON,
  timestamp TIMESTAMP,
  event_name STRING,
  event_value STRING,
  user_object JSON,
  statsig_metadata JSON,
  company_metadata JSON
);
```

**Exposures**

By default, Statsig doesn't export exposures from rules that are 0%/100%. See [Why a day is missing](#why-a-day-is-missing) for the exception.

| Field name | Type | Mode |
| --- | --- | --- |
| company\_id | STRING | NULLABLE |
| unit\_id | STRING | NULLABLE |
| unit\_type | STRING | NULLABLE |
| exposure\_type | STRING | NULLABLE |
| name | STRING | NULLABLE |
| rule | STRING | NULLABLE |
| experiment\_group | STRING | NULLABLE |
| first\_exposure\_utc | TIMESTAMP | NULLABLE |
| first\_exposure\_pst\_date | DATE | NULLABLE |
| as\_of\_pst\_date | DATE | NULLABLE |
| percent | FLOAT64 | NULLABLE |
| rollout | BIGINT | NULLABLE |
| user\_dimensions | STRING | NULLABLE |
| inserted\_at | TIMESTAMP | NULLABLE |
| rule\_name | STRING | NULLABLE |
| group\_id | STRING | NULLABLE |
| non\_analytics | BOOLEAN | NULLABLE |

**Events**

| Field name | Type | Mode |
| --- | --- | --- |
| user\_id | STRING | NULLABLE |
| stable\_id | STRING | NULLABLE |
| custom\_ids | JSON | NULLABLE |
| timestamp | TIMESTAMP | NULLABLE |
| event\_name | STRING | NULLABLE |
| event\_value | STRING | NULLABLE |
| user\_object | JSON | NULLABLE |
| statsig\_metadata | JSON | NULLABLE |
| company\_metadata | JSON | NULLABLE |

#### Snowflake

**Setup summary**

Configure:

- Account Name
- Database Name
- Schema Name
- either Username and Password, or Private Key authentication
- one destination table name per export type

Permissions:

- The configured user must be able to create tables and insert/delete rows in the target database and schema.

If you prefer to create the export tables yourself, you can use SQL like this:

```sql
CREATE TABLE IF NOT EXISTS DATABASE_NAME.SCHEMA_NAME.EXPOSURES_TABLE (
  company_id STRING,
  unit_id STRING,
  unit_type STRING,
  exposure_type STRING,
  name STRING,
  rule STRING,
  experiment_group STRING,
  first_exposure_utc TIMESTAMP,
  first_exposure_pst_date DATE,
  as_of_pst_date DATE,
  percent DOUBLE,
  rollout BIGINT,
  user_dimensions STRING,
  inserted_at TIMESTAMP,
  rule_name STRING,
  group_id STRING,
  non_analytics BOOLEAN
);
```

```sql
CREATE TABLE IF NOT EXISTS DATABASE_NAME.SCHEMA_NAME.EVENTS_TABLE (
  user_id STRING,
  stable_id STRING,
  custom_ids STRING,
  timestamp TIMESTAMP,
  event_name STRING,
  event_value STRING,
  user_object STRING,
  statsig_metadata STRING,
  company_metadata STRING
);
```

**Exposures**

| Field name | Type | Mode |
| --- | --- | --- |
| company\_id | STRING | NULLABLE |
| unit\_id | STRING | NULLABLE |
| unit\_type | STRING | NULLABLE |
| exposure\_type | STRING | NULLABLE |
| name | STRING | NULLABLE |
| rule | STRING | NULLABLE |
| experiment\_group | STRING | NULLABLE |
| first\_exposure\_utc | TIMESTAMP | NULLABLE |
| first\_exposure\_pst\_date | DATE | NULLABLE |
| as\_of\_pst\_date | DATE | NULLABLE |
| percent | DOUBLE | NULLABLE |
| rollout | BIGINT | NULLABLE |
| user\_dimensions | STRING | NULLABLE |
| inserted\_at | TIMESTAMP | NULLABLE |
| rule\_name | STRING | NULLABLE |
| group\_id | STRING | NULLABLE |
| non\_analytics | BOOLEAN | NULLABLE |

**Events**

| Field name | Type | Mode |
| --- | --- | --- |
| user\_id | STRING | NULLABLE |
| stable\_id | STRING | NULLABLE |
| custom\_ids | STRING | NULLABLE |
| timestamp | TIMESTAMP | NULLABLE |
| event\_name | STRING | NULLABLE |
| event\_value | STRING | NULLABLE |
| user\_object | STRING | NULLABLE |
| statsig\_metadata | STRING | NULLABLE |
| company\_metadata | STRING | NULLABLE |

#### Redshift

**Setup summary**

Configure:

- Cluster Endpoint
- Username and Password
- Staging Schema
- one destination table name per export type

Permissions:

- The configured user must be able to create tables and insert/delete rows in the target database and schema.
- If your cluster isn't directly reachable, you can also configure SSH tunneling.

If you prefer to create the export tables yourself, you can use SQL like this:

```sql
CREATE TABLE IF NOT EXISTS "DATABASE_NAME"."STAGING_SCHEMA"."EXPOSURES_TABLE" (
  "company_id" VARCHAR,
  "unit_id" VARCHAR,
  "unit_type" VARCHAR,
  "exposure_type" VARCHAR,
  "name" VARCHAR,
  "rule" VARCHAR,
  "experiment_group" VARCHAR,
  "first_exposure_utc" TIMESTAMP,
  "first_exposure_pst_date" DATE,
  "as_of_pst_date" DATE,
  "percent" DOUBLE PRECISION,
  "rollout" BIGINT,
  "user_dimensions" VARCHAR,
  "inserted_at" TIMESTAMP,
  "rule_name" VARCHAR,
  "group_id" VARCHAR,
  "non_analytics" BOOLEAN
);
```

```sql
CREATE TABLE IF NOT EXISTS "DATABASE_NAME"."STAGING_SCHEMA"."EVENTS_TABLE" (
  "user_id" VARCHAR,
  "stable_id" VARCHAR,
  "custom_ids" VARCHAR,
  "timestamp" TIMESTAMP,
  "event_name" VARCHAR,
  "event_value" VARCHAR,
  "user_object" VARCHAR,
  "statsig_metadata" VARCHAR,
  "company_metadata" VARCHAR
);
```

> **Note:**
>
> The default size for VARCHAR is 256 and Statsig truncates all columns to 256 characters for Redshift. If you anticipate certain columns exceeding these limits, adjust accordingly and contact Statsig to remove these limits. Common longer columns are `user_dimensions` in exposures and `user_object`, `custom_ids`, and `company_metadata` in events.

**Exposures**

| Field name | Type | Mode |
| --- | --- | --- |
| company\_id | VARCHAR | NULLABLE |
| unit\_id | VARCHAR | NULLABLE |
| unit\_type | VARCHAR | NULLABLE |
| exposure\_type | VARCHAR | NULLABLE |
| name | VARCHAR | NULLABLE |
| rule | VARCHAR | NULLABLE |
| experiment\_group | VARCHAR | NULLABLE |
| first\_exposure\_utc | TIMESTAMP | NULLABLE |
| first\_exposure\_pst\_date | DATE | NULLABLE |
| as\_of\_pst\_date | DATE | NULLABLE |
| percent | DOUBLE PRECISION | NULLABLE |
| rollout | BIGINT | NULLABLE |
| user\_dimensions | VARCHAR | NULLABLE |
| inserted\_at | TIMESTAMP | NULLABLE |
| rule\_name | VARCHAR | NULLABLE |
| group\_id | VARCHAR | NULLABLE |
| non\_analytics | BOOLEAN | NULLABLE |

**Events**

| Field name | Type | Mode |
| --- | --- | --- |
| user\_id | VARCHAR | NULLABLE |
| stable\_id | VARCHAR | NULLABLE |
| custom\_ids | VARCHAR | NULLABLE |
| timestamp | TIMESTAMP | NULLABLE |
| event\_name | VARCHAR | NULLABLE |
| event\_value | VARCHAR | NULLABLE |
| user\_object | VARCHAR | NULLABLE |
| statsig\_metadata | VARCHAR | NULLABLE |
| company\_metadata | VARCHAR | NULLABLE |

#### Databricks

**Setup summary**

Configure:

- API Key
- Server Hostname
- HTTP Path
- Database
- one destination table name per export type

Permissions:

- The configured user or token must be able to create tables and insert/delete rows in the target database.

If you prefer to create the export tables yourself, you can use SQL like this:

```sql
CREATE TABLE IF NOT EXISTS DATABASE_NAME.EXPOSURES_TABLE (
  company_id STRING,
  unit_id STRING,
  unit_type STRING,
  exposure_type STRING,
  name STRING,
  rule STRING,
  experiment_group STRING,
  first_exposure_utc TIMESTAMP,
  first_exposure_pst_date DATE,
  as_of_pst_date DATE,
  percent DOUBLE,
  rollout BIGINT,
  user_dimensions STRING,
  inserted_at TIMESTAMP,
  rule_name STRING,
  group_id STRING,
  non_analytics BOOLEAN
);
```

```sql
CREATE TABLE IF NOT EXISTS DATABASE_NAME.EVENTS_TABLE (
  user_id STRING,
  stable_id STRING,
  custom_ids STRING,
  timestamp TIMESTAMP,
  event_name STRING,
  event_value STRING,
  user_object STRING,
  statsig_metadata STRING,
  company_metadata STRING
);
```

**Exposures**

| Field name | Type | Mode |
| --- | --- | --- |
| company\_id | STRING | NULLABLE |
| unit\_id | STRING | NULLABLE |
| unit\_type | STRING | NULLABLE |
| exposure\_type | STRING | NULLABLE |
| name | STRING | NULLABLE |
| rule | STRING | NULLABLE |
| experiment\_group | STRING | NULLABLE |
| first\_exposure\_utc | TIMESTAMP | NULLABLE |
| first\_exposure\_pst\_date | DATE | NULLABLE |
| as\_of\_pst\_date | DATE | NULLABLE |
| percent | DOUBLE | NULLABLE |
| rollout | BIGINT | NULLABLE |
| user\_dimensions | STRING | NULLABLE |
| inserted\_at | TIMESTAMP | NULLABLE |
| rule\_name | STRING | NULLABLE |
| group\_id | STRING | NULLABLE |
| non\_analytics | BOOLEAN | NULLABLE |

**Events**

| Field name | Type | Mode |
| --- | --- | --- |
| user\_id | STRING | NULLABLE |
| stable\_id | STRING | NULLABLE |
| custom\_ids | STRING | NULLABLE |
| timestamp | TIMESTAMP | NULLABLE |
| event\_name | STRING | NULLABLE |
| event\_value | STRING | NULLABLE |
| user\_object | STRING | NULLABLE |
| statsig\_metadata | STRING | NULLABLE |
| company\_metadata | STRING | NULLABLE |

#### S3

**Setup summary**

Configure:

- Region
- Bucket
- one folder name per export type

Permissions:

- Grant the Statsig IAM user bucket-level access to list the bucket and retrieve its location.
- Grant object-level read, write, and delete permissions, including multipart upload management.

S3 exports are file-based. Statsig writes export files to the configured bucket and folder for each export type. In the export UI, you configure the per-export destination as a folder rather than a table.

**Export outputs**

| Export type | Destination shape |
| --- | --- |
| Exposures | Files written to the configured folder in your bucket |
| Events | Files written to the configured folder in your bucket |

## Monitor your export status

Statsig reports warehouse export status through notifications, the Statsig Console, the Console API, and [Statsig MCP](https://docs.statsig.com/integrations/mcp/overview). Use notifications for failures. Use the Console, the API, or MCP when you need to check a specific export run.

### Export notifications

By default, a failed export sends an in-app bell notification and an email to members with the Admin role and the member who configured the integration. If the integration has an explicit subscriber list, Statsig notifies those subscribers instead. Statsig doesn't resend a notification for the same failure on the same day.

Each recipient can turn export failure emails off or back on under _Settings > My Account > Notifications > Email Notifications_. Click **Edit**, then use **Alerts** ("Import, export, and health check failures"). This setting is per user. Turning **Alerts** off doesn't stop in-app notifications.

Successful exports don't send notifications by default. Contact Statsig support to enable success notifications for your project.

### Check export status in the Statsig Console

For scheduled data warehouse exports, go to _Help and Tools > Exports List > Scheduled_ and find the connection card. The Exposures and Events charts show recent export runs.

For Warehouse Native forwarded data, go to _Settings > Data Connection > Export_. Use the _Export Exposures History_ and _Export Events History_ charts.

- `Rows inserted` or `File created` indicates a successful export.
- `Error` or `Internal server error` indicates a failed export.
- Hover over a day to view the latest run's status, row count, and report time for that day. For a failed export, the tooltip also shows the failure reason.

### Check export status with the Console API

Send a `GET` request to `/console/v1/exports/status` with a Console API key. `startDate` and `endDate` are required (`YYYY-MM-DD`, UTC, inclusive). The range can be at most 90 days. `dataset` is optional: `exposures`, `events`, or omit it to return both. `status` is optional: `success` or `failed`. `page` and `limit` page the results. `limit` can be at most 100. Statsig returns the newest rows first. Without `limit`, a page holds your project's default page size, usually 100.

An invalid date, a start date after the end date, or a range longer than 90 days returns HTTP 400. A missing or invalid Console API key returns HTTP 401. A range with no matching rows returns HTTP 200 and an empty `data` array.

`status` is `success` or `failed`. `status_code` is the underlying code, such as `ROWS_INSERTED`, `FILE_CREATED`, `ERROR`, `EXPORT_ERROR`, or `INTERNAL_SERVER_ERROR`. `message` is a human-readable description of the run. It can be one or more sentences or `null`, and for a failure it may include the reported warehouse error. `rows_exported` is present when Statsig recorded a row count.

The older `GET /console/v1/ingestion/status` endpoint still works with `dataset=export_exposures` or `dataset=export_events`. Prefer `/console/v1/exports/status`.

```bash
curl --get 'https://statsigapi.net/console/v1/exports/status' \
  --header 'STATSIG-API-KEY: console-example-key' \
  --data-urlencode 'startDate=2026-09-17' \
  --data-urlencode 'endDate=2026-09-30' \
  --data-urlencode 'dataset=exposures' \
  --data-urlencode 'status=failed'
```

```json
{
  "message": "Export statuses listed successfully.",
  "data": [
    {
      "date": "2026-09-21",
      "dataset": "exposures",
      "destination": { "type": "bigquery" },
      "status": "failed",
      "status_code": "ERROR",
      "message": "Your BigQuery export_exposures export for 2026-09-21 failed. Missing GET permission on table my_project:my_dataset.exposures",
      "reported_at": "2026-09-22T09:14:22.000Z"
    }
  ],
  "pagination": {
    "itemsPerPage": 100,
    "pageNumber": 1,
    "totalItems": 1,
    "nextPage": null,
    "previousPage": null
  }
}
```

### Ask about export status with Statsig MCP

[Statsig MCP](https://docs.statsig.com/integrations/mcp/overview) lets an AI assistant read export status for projects the connected account can access. Set it up in [Claude](https://docs.statsig.com/integrations/mcp/claude-code), [ChatGPT](https://docs.statsig.com/integrations/mcp/chatgpt-connector), or [Codex](https://docs.statsig.com/integrations/mcp/codex).

Paste a prompt like one of these:

- I think our export failed sometime in the last two weeks. Can you check the reason?
- Did yesterday's exposures export succeed?
- Why did my events export fail on 2026-09-21?
- Which days in the last two weeks have no successful export?

A typical answer names the dates, the dataset, and the warehouse error. For example: event exports failed on two dates because the exporting identity has no GET access to `my_project:my_dataset.events`, and exposure exports failed on two dates for `my_project:my_dataset.exposures`. A later exposure export for one date succeeded and wrote 120 rows. Check that identity's table access and the destination row counts before you ask Statsig to replay those days.

The export-status operation in Statsig MCP is read-only. It reports status and may include the reported warehouse error, but it can't grant warehouse permissions or replay exports. Keep each question to a short date range. The API accepts up to 90 days.

If the message says access is missing, restore the identity's Read, Write, and Delete permissions, then contact Statsig support if you need a replay.

### Why a day is missing

Statsig exports exposures daily. For scheduled data warehouse exports, events also export daily. For Warehouse Native forwarded data, events export hourly. A missing status alone can't tell you whether the job ran or whether data reached the destination. Check the destination for the expected rows, and contact Statsig support if you expected data but can't confirm a successful export.

By default, Statsig doesn't export exposures from feature gates with 0% or 100% rollout or from experiment variants with 0% or 100% traffic allocation. For gates, a project admin can turn on **Enable exposure tracking for 0/100% rolled out Gates** in _Settings > Project Configuration > Feature Management_, then turn on **Store 0/100 Exposures** on each gate that needs these exposures. Statsig bills these exposures as analytic events. For experiments, contact Statsig support.

### Resolve common export failures

| Failure cause | What to do |
| --- | --- |
| Missing warehouse permissions | Restore the configured identity's Read, Write, and Delete permissions for the destination. |
| Destination table was dropped or renamed | Restore the table or update the export configuration to use the new table name. |
| Credentials were rotated or disabled | Update the data connection with active credentials. |
| Network policy blocks Statsig | Add the [Statsig IP ranges](https://docs.statsig.com/infrastructure/statsig_ip_ranges) to your allow list. |
| Warehouse has insufficient capacity | Resume the warehouse or increase its available compute capacity. |

If the export continues to fail after you check these causes, contact Statsig support.

> **Info:**
>
> Reliable exports matter, and so does knowing their status. If you need more visibility into your exposure or event exports than this page covers, contact [Statsig Support](https://docs.statsig.com/support-options) or post in the [Slack community](https://statsig.com/slack). Statsig uses that feedback to keep improving export observability.

