# 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`.
- 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`. 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`. The response is a `ReportRun` with `status=running` and no file yet. 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`.

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 include the fully resolved SQL that ran, add `include[0]=sql`.

```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"
```

## 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 SQL generated from the report definition and parameters.

- The same `format` and `result_options.compress_file` settings.
- A run that’s still `running` or that `succeeded` less than seven days ago.
- The same run-level data freshness timestamp determined by Stripe.

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 report run results for 90 days. |
| 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)
