# Run a report

Generate a Stripe-defined report programmatically.

You can generate Stripe-defined reports and Sigma query templates from your own systems. List the reports your account can run, inspect the parameters a report accepts, then create a `ReportRun`. You can scope a run to one account or to multiple accounts in a Stripe Organization.

A `ReportRun` is an asynchronous job: you pass a report name and parameters, Stripe starts the report, and you [retrieve the ReportRun](https://docs.stripe.com/data/api/reports.md#retrieve-report-run) later for a download link or inline rows. Learn more about [runs](https://docs.stripe.com/data/api.md#concepts). You don’t supply SQL for a report. Use a [query run](https://docs.stripe.com/data/api/query-runs.md) instead when you want to send your own SQL.

## Required permissions 

To list reports and to create and retrieve report runs, grant **Data** > **Reports** > **Read** (`data_reports_read`) on a [restricted API key](https://dashboard.stripe.com/apikeys).

## List available reports

Use this endpoint to list the Stripe-defined reports and Sigma query templates available to your account. The response includes each report’s `id`, `name`, and `description`. It also includes `parameters`, which is `null` unless you include it (see below).

Use the `include` parameter to return additional report fields:

- To include representative SQL, set `include[0]=default_sql`. If the SQL can’t be exposed, `default_sql` contains an explanatory message instead.
- To include parameter definitions, set `include[0]=parameters`.
- To include both fields, set `include[0]=parameters` and `include[1]=default_sql`.

The API returns `null` for `default_sql` and `parameters` unless you explicitly include them.

By default, the API returns 10 reports per page. Set `limit` to return 1–1,000 reports per page.

To filter by an exact, case-sensitive report name, set `name` (for example, `name=balance.summary`).

To retrieve a specific report, see [Retrieve a report template](https://docs.stripe.com/data/api/reports.md#retrieve-report).

```curl
curl https://api.stripe.com/v2/data/reports \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-09-30.preview"
```

```json
{
  "object": "v2.list",
  "data": [
    {
      "id": "report_61Sud3n5oAGVCWiSr5",
      "object": "v2.data.report",
      "name": "balance.summary",
      "description": "Your starting balance, activity, payouts and ending balance for the date range. Amounts are based on when transactions affected your Stripe balance. Use this summary report like a bank statement to reconcile your opening balance to your closing balance.",
      "livemode": true,
      "default_sql": null,
      "parameters": null
    }
  ],
  "next_page_url": null,
  "previous_page_url": null
}
```

## Retrieve a report template

Retrieve a report to see its name, description, and the required and optional parameters it accepts, including field-level validations. Inspect the template before you create a `ReportRun`.

Unlike the list endpoint, the retrieve endpoint returns a report’s parameter definitions by default. You don’t need to set `include[0]=parameters`.

To include representative SQL built from common parameter values, add `include[0]=default_sql`. If the SQL can’t be exposed, `default_sql` contains an explanatory message instead. The field is only present when explicitly requested. Different parameter values can generate different SQL, and `default_sql` won’t always match the exact SQL that generated your report results. To get the exact `sql` used to populate your report, retrieve the `ReportRun` with `include[0]=sql`. See [Access results after the ReportRun completes](https://docs.stripe.com/data/api/reports.md#retrieve-report-run).

```curl
curl -G https://api.stripe.com/v2/data/reports/report_61Sud3n5oAGVCWiSr5 \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-09-30.preview" \
  -d "include[0]=default_sql"
```

```json
{
  "id": "report_61Sud3n5oAGVCWiSr5",
  "object": "v2.data.report",
  "name": "balance.summary",
  "description": "Your starting balance, activity, payouts and ending balance for the date range. Amounts are based on when transactions affected your Stripe balance. Use this summary report like a bank statement to reconcile your opening balance to your closing balance.",
  "livemode": true,
  "default_sql": "SELECT ...",
  "parameters": {
    "interval_start": {
      "description": "Starting timestamp of data to be included.",
      "required": true,
      "type": "timestamp"
    },
    "interval_end": {
      "description": "Ending timestamp of data to be included.",
      "required": true,
      "type": "timestamp"
    }
  }
}
```

## Create a ReportRun

Create a `ReportRun` with a report reference and the parameters that report accepts. You can identify the report by `id` or by `name`. Stripe starts the report and returns a `ReportRun` immediately. A new run has `status=running` and no file yet. If an identical run already exists, Stripe returns that run instead (see [Understand cached runs](https://docs.stripe.com/data/api/reports.md#cached-runs)). The returned run might already have `status=succeeded` with a file you can download right away. Check `status` in the create response before you poll or wait for a webhook. Save the `id`, then [retrieve the ReportRun](https://docs.stripe.com/data/api/reports.md#retrieve-report-run) after it completes.

```curl
curl -X POST https://api.stripe.com/v2/data/report_runs \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-09-30.preview" \
  --json '{
    "report": {
        "name": "balance.summary"
    },
    "parameters": {
        "interval_start": "2026-06-01T00:00:00.000Z",
        "interval_end": "2026-07-01T00:00:00.000Z"
    },
    "format": "csv"
  }'
```

In live mode, this request is equivalent to passing `"report": {"id": "report_61Sud3n5oAGVCWiSr5"}`. In a sandbox, use `"report": {"id": "report_test_61Sud3nd37HpUFYD75"}`.

## Access results after the ReportRun completes

When the run succeeds, Stripe sends a `v2.data.report_run.succeeded` event. When it fails, Stripe sends a `v2.data.report_run.failed` event. After receiving the event, retrieve the `ReportRun`. If `status` is `succeeded`, read `result.file.download_url.url`. If `status` is `failed`, read `status_details`. If you poll instead of listening for events, continue until `status` is `succeeded`, `failed`, or `canceled`. Stripe might add new statuses, so don’t assume this set of values is fixed.

The `file_size_above_limit` code indicates that the result exceeded 5 GB. The `internal_error` code indicates another failure. Read `status_details.message` for details.

Before processing these events, [verify incoming webhook signatures](https://docs.stripe.com/webhooks.md#verify-events) and review the Stripe [public IP addresses](https://docs.stripe.com/ips.md).

```curl
curl https://api.stripe.com/v2/data/report_runs/rrun_123 \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-09-30.preview"
```

```json
{
  "id": "rrun_123",
  "object": "v2.data.report_run",
  "created": "2026-07-03T01:02:29.964Z",
  "report": "report_61Sud3n5oAGVCWiSr5",
  "name": "balance.summary",
  "parameters": {
    "interval_start": "2026-06-01T00:00:00.000Z",
    "interval_end": "2026-07-01T00:00:00.000Z"
  },
  "result": {
    "file": {
      "content_type": "csv",
      "download_url": {
        "expires_at": "2026-07-03T01:10:46.679Z",
        "url": "https://stripeusercontent.com/files/us-west-2/download/wksp_123/file_123/rrun_123.csv..."
      },
      "size": "209"
    }
  },
  "result_options": {
    "compress_file": false
  },
  "status": "succeeded",
  "livemode": false
}
```

> The download URL expires after 5 minutes. Retrieve the `ReportRun` again to get a new URL.

To page through rows in the API response, retrieve the run with `include[0]=result.inline`. Use `limit` (default 100, maximum 1,000) and follow `next_page_url`. Keep the same `limit` on every request in a pagination sequence. To use a different `limit`, start pagination again without a page token. To request the fully resolved SQL that ran, add `include[0]=sql`. If the SQL can’t be exposed, the `sql` field contains an explanatory message instead. The field is only present when explicitly requested.

```curl
curl -G https://api.stripe.com/v2/data/report_runs/rrun_123 \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-09-30.preview" \
  -d "include[0]=result.inline" \
  -d "include[1]=sql"
```

Stripe retains a `ReportRun` and its results for 90 days after the run completes. After 90 days, the results expire and attempts to retrieve the `ReportRun` return a `404` error. To regenerate the results, create a new `ReportRun`. To retain results for longer than 90 days, download and store them.

## Understand cached runs 

We cache report runs to avoid repeating the same execution. A matching create request can return an existing `ReportRun` with the same `id` and `created` timestamp instead of creating another run.

A cache match requires all of the following:

- The same report and parameter values.
- The same Stripe account or [organization](https://docs.stripe.com/get-started/account/orgs.md), and the same set of accounts you’re authorized to include in the report.
- The same live mode or [sandbox](https://docs.stripe.com/sandboxes.md) context.
- The same generated SQL. We generate the SQL for a run from the report definition and your parameter values. If an update to the report’s definition changes the SQL generated for your parameters, your next request creates a new run. To see the SQL a run used, retrieve it with `include[0]=sql`.

- The same `format` and `result_options.compress_file` settings.
- The same `refreshed_at` value. This value indicates when Stripe last refreshed the data used by the run. After Stripe refreshes your data, the next matching request creates a new run with the updated data.
- A run that’s still `running` or that `succeeded` less than seven days ago.

The returned run is either `succeeded` or still `running` because an identical run is in progress.

Reusing a run doesn’t emit another `v2.data.report_run.created` event. If the returned run is already `succeeded`, use its results without waiting for another completion event.

## Request file compression 

For large result sets, set `result_options.compress_file` to `true` when you create the run.

```curl
curl -X POST https://api.stripe.com/v2/data/report_runs \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-09-30.preview" \
  --json '{
    "report": {
        "name": "balance.summary"
    },
    "parameters": {
        "interval_start": "2026-06-01T00:00:00.000Z",
        "interval_end": "2026-07-01T00:00:00.000Z"
    },
    "format": "csv",
    "result_options": {
        "compress_file": true
    }
  }'
```

## Use an Organization API key 

The Reports API accepts [Organization API keys](https://docs.stripe.com/keys/organization-api-keys.md) alongside account-level keys.

### Request a report for a single direct account

Set [Stripe-Context](https://docs.stripe.com/context.md) to the direct account. You don’t need that account’s API key.

```curl
curl -X POST https://api.stripe.com/v2/data/report_runs \
  -H "Authorization: Bearer sk_org_123" \
  -H "Stripe-Version: 2026-09-30.preview" \
  -H "Stripe-Context: {{CONTEXT_ID}}" \
  --json '{
    "report": {
        "name": "balance.summary"
    },
    "parameters": {
        "interval_start": "2026-06-01T00:00:00.000Z",
        "interval_end": "2026-07-01T00:00:00.000Z"
    },
    "format": "csv"
  }'
```

### Request a report for a connected account

Some reports accept a `connected_account` parameter. Set `Stripe-Context` to the platform that owns the connected account.

```curl
curl -X POST https://api.stripe.com/v2/data/report_runs \
  -H "Authorization: Bearer sk_org_123" \
  -H "Stripe-Version: 2026-09-30.preview" \
  -H "Stripe-Context: {{CONTEXT_ID}}" \
  --json '{
    "report": {
        "name": "balance.summary"
    },
    "parameters": {
        "connected_account": "acct_123",
        "interval_start": "2026-06-01T00:00:00.000Z",
        "interval_end": "2026-07-01T00:00:00.000Z"
    },
    "format": "csv"
  }'
```

### Request multi-account reports

You can run reports across an organization in concatenated or aggregated mode. Every report supports concatenated mode. Some reports also support aggregated mode. If a report supports both, set the `organization_report_granularity` parameter to `concatenated_report` or `aggregated_report`, and omit `Stripe-Context`.

```curl
curl -X POST https://api.stripe.com/v2/data/report_runs \
  -H "Authorization: Bearer sk_org_123" \
  -H "Stripe-Version: 2026-09-30.preview" \
  --json '{
    "report": {
        "name": "balance.summary"
    },
    "parameters": {
        "organization_report_granularity": "aggregated_report",
        "interval_start": "2026-06-01T00:00:00.000Z",
        "interval_end": "2026-07-01T00:00:00.000Z"
    },
    "format": "csv"
  }'
```

If the report only supports concatenated mode, omit `organization_report_granularity`.

## Limits 

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

|  |
| Concurrent report runs | 500 `ReportRun` objects can be `running` at the same time in live mode, and 100 in a *sandbox* (A sandbox is an isolated test environment that allows you to test Stripe functionality in your account without affecting your live integration. Use sandboxes to safely experiment with new features and changes). Requests that exceed the limit return a `429` status code. |
| File size | 5 GB per result file. Request `result_options.compress_file=true` if you reach this limit. |
| File retention | Stripe retains a `ReportRun` and its results for 90 days after the run completes. After 90 days, the results expire. To regenerate the results, create a new `ReportRun`. |
| Download URL lifetime | File download URLs expire after 5 minutes. |

## See also

- [Access Stripe data with the API](https://docs.stripe.com/data/api.md)
- [Browse table schemas](https://docs.stripe.com/data/api/schemas.md)
- [Run a SQL query](https://docs.stripe.com/data/api/query-runs.md)
