# Run a SQL Query from the Reports API v2 Execute ad-hoc SQL queries against your Stripe data programmatically. You can programmatically execute SQL queries against your Stripe data using the `QueryRun` endpoints of the v2 Reports API. This allows you to run the same queries available in the [Sigma editor](https://docs.stripe.com/stripe-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/stripe-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 <>" \ -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 <>" \ -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 <>" \ -H "Stripe-Version: 2026-04-22.preview" \ --json '{ "sql": "SELECT * FROM balance_transactions", "result_options": { "compress_file": true } }' ``` ## Use an Organisation API key The Query Run API supports [Organisation API keys](https://docs.stripe.com/keys.md#organization-api-keys). ### Run a query across your entire organisation When you use an Organisation API key without a `Stripe-Context` header, the query runs in full organisation mode – equivalent to running a query in [Sigma for Organisations](https://docs.stripe.com/stripe-data/sigma-organizations.md). In this mode, your query has access to data across all direct accounts in your organisation, 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 organisation, 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" }' ``` ## 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.