# Run a SQL query programmatically

Execute ad-hoc SQL queries from the Query Run API.

You can programmatically execute SQL queries against your Stripe data using the `QueryRun` endpoints of the Reports API v2. This allows you to run the same queries available in the [Sigma editor](https://docs.stripe.com/data/write-queries.md) without using the Dashboard, enabling you to automate data extraction, schedule queries at custom intervals, and integrate Stripe data into your own systems.

> #### Using the Query Run API
> 
> The Query Run API requires an active [Sigma](https://stripe.com/sigma) subscription.

## API key permissions

To create query runs, your API key must have both the `reporting_write` and `sigma_api_write` permissions. If your key only has `reporting_read`, you can retrieve existing query runs but can’t create new ones. Use the [API keys](https://dashboard.stripe.com/apikeys) page to view and manage permissions. When creating a restricted key in the Dashboard, `reporting_write` appears as **Sigma** and `sigma_api_write` appears as **Sigma API**.

## Create a QueryRun

[Create a QueryRun](https://docs.stripe.com/api/v2/data/reporting/query-runs/create.md?api-version=2026-04-22.preview) by providing a SQL statement in the `sql` parameter. Use the same SQL syntax supported by the [Sigma query editor](https://docs.stripe.com/data/write-queries.md). The response always includes a new [QueryRun object](https://docs.stripe.com/api/v2/data/reporting/query-runs/object.md?api-version=2026-04-22.preview) with `status=running` and `result=null`.

```curl
curl -X POST https://api.stripe.com/v2/data/reporting/query_runs \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-04-22.preview" \
  --json '{
    "sql": "SELECT * FROM balance_transactions LIMIT 10"
  }'
```

```json
{
  "id": "qryrun_123",
  "object": "v2.data.reporting.query_run",
  "created": "2025-07-03T01:02:29.964Z",
  "sql": "SELECT * FROM balance_transactions LIMIT 10",
  "status": "running",
  "result": null,
  "result_options": {
    "compress_file": false,
    "result_type": "file"
  },
  "livemode": false
}
```

Use the returned `id` to track the progress of the query run.

## Retrieve a QueryRun

[Retrieve a QueryRun](https://docs.stripe.com/api/v2/data/reporting/query-runs/retrieve.md?api-version=2026-04-22.preview) to check its status. When the query completes, access the results with the URL in `result.file.download_url.url`.

```curl
curl https://api.stripe.com/v2/data/reporting/query_runs/qryrun_123 \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-04-22.preview"
```

```json
{
  "id": "qryrun_123",
  "object": "v2.data.reporting.query_run",
  "created": "2025-07-03T01:02:29.964Z",
  "sql": "SELECT * FROM balance_transactions LIMIT 10",
  "status": "succeeded",
  "result": {
    "file": {
      "content_type": "csv",
      "download_url": {
        "expires_at": "2025-07-03T01:10:46.679Z",
        "url": "https://stripeusercontent.com/files/us-west-2/download/wksp_123/file_123/qryrun_123.csv..."
      },
      "size": "512"
    },
    "type": "file"
  },
  "result_options": {
    "compress_file": false,
    "result_type": "file"
  },
  "livemode": false
}
```

> The download URL is short-lived and expires after 5 minutes. If you need to regenerate it, retrieve the `QueryRun` object again.

## Webhooks

Instead of polling, you can listen for webhooks to know when a query completes. Stripe sends a [v2.data.reporting.query_run.succeeded](https://docs.stripe.com/api/v2/data/reporting/query-runs/event-types.md?api-version=2026-04-22.preview#v2.data.reporting.query_run.succeeded) webhook when the query run completes successfully. In case of failure, Stripe sends a [v2.data.reporting.query_run.failed](https://docs.stripe.com/api/v2/data/reporting/query-runs/event-types.md?api-version=2026-04-22.preview#v2.data.reporting.query_run.failed) webhook instead. After receiving the webhook, retrieve the `QueryRun` and access the result URL as described above.

## Additional result options

### Request file compression

For large result sets, set `result_options.compress_file=true` to receive a zip-compressed output file.

```curl
curl -X POST https://api.stripe.com/v2/data/reporting/query_runs \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-04-22.preview" \
  --json '{
    "sql": "SELECT * FROM balance_transactions",
    "result_options": {
        "compress_file": true
    }
  }'
```

## Use an Organization API key

The Query Run API supports [Organization API keys](https://docs.stripe.com/keys/organization-api-keys.md).

### Run a query across your entire organization

When you use an Organization API key without a `Stripe-Context` header, the query runs in full organization mode—equivalent to running a query in [Sigma for Organizations](https://docs.stripe.com/data/sigma-organizations.md). In this mode, your query has access to data across all direct accounts in your organization, and an `account` column is available in data tables to identify which account each row belongs to.

```curl
curl -X POST https://api.stripe.com/v2/data/reporting/query_runs \
  -H "Authorization: Bearer {{ORG_SECRET_KEY}}" \
  -H "Stripe-Version: 2026-04-22.preview" \
  --json '{
    "sql": "SELECT account, id, amount FROM balance_transactions LIMIT 10"
  }'
```

### Run a query scoped to a single account

To run a query scoped to a single account in your organization, set the [Stripe-Context](https://docs.stripe.com/context.md) header to that account.

```curl
curl -X POST https://api.stripe.com/v2/data/reporting/query_runs \
  -H "Authorization: Bearer {{ORG_SECRET_KEY}}" \
  -H "Stripe-Version: 2026-04-22.preview" \
  -H "Stripe-Context: {{CONTEXT_ID}}" \
  --json '{
    "sql": "SELECT * FROM balance_transactions LIMIT 10"
  }'
```

## Stripe MCP 

You can use the [Stripe Model Context Protocol (MCP) server](https://docs.stripe.com/mcp.md)’s analytics tool to query Sigma data from an AI agent or code editor. The MCP tool calls the endpoint for you, and you can build custom reports and reason with your data in natural language instead of manually constructing API requests.

## Limits

For general details about APIs in the v2 namespace, see the [API v2 overview](https://docs.stripe.com/api-v2-overview.md). For general API rate limits, see the [Rate limits](https://docs.stripe.com/rate-limits.md) page. Specific limits for the Query Run API include:

### Concurrent query runs

The API limits the number of `QueryRuns` that can be in `running` state simultaneously to 500 in livemode and 100 in testmode per account or organization. If you exceed this limit, the API responds with a status code of `429`. You can free up capacity by waiting for running queries to complete.

### Query execution timeout

Queries that exceed 90 minutes of execution time are automatically terminated and the `QueryRun` moves to `status=failed`.

### File size

The maximum supported file size for a query result is 5 GB. If you reach this limit, request compressed results by setting `result_options.compress_file=true`.

### File retention

Query run results are retained for 90 days.
