# 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 <>" \ -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 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.