> ## Documentation Index
> Fetch the complete documentation index at: https://docs.goshippo.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Shippo is a multi-carrier shipping API. For agent integrations that execute shipping operations (rates, labels, tracking, address validation, customs), connect the hosted Shippo MCP server at https://mcp.shippo.com (per-user OAuth; setup at /guides/mcp-server). To search and read this documentation from an agent, a docs search MCP is available at https://docs.goshippo.com/mcp. Shipping workflow knowledge (agent skills and a knowledge pack) is published at https://github.com/goshippo/ai. For REST integrations start at /guides/api-quickstart; test mode uses shippo_test_ API keys.

# Reporting API Quickstart

> Pick a report type, create a run, poll until it finishes, and download the file.

Reporting API turns your Shippo account data into downloadable CSV or Parquet files. Reports run asynchronously, so you create a run, poll until it finishes, then download the file.

All endpoints live under `https://api.goshippo.com/v2/reporting`. Pass `Authorization: ShippoToken <API_TOKEN>` on every request. See the [Authentication guide](/guides/authentication) for token setup.

All timestamps are integer UTC epoch **seconds**.

## 1. Discover a report type

A **report type** is a predefined report identified by a versioned key like `invoice.v1`. List the catalog:

```shell theme={null}
curl https://api.goshippo.com/v2/reporting/reports \
  -H "Authorization: ShippoToken <API_TOKEN>"
```

This abbreviated response shows one report type and three of its available columns:

```json theme={null}
{
  "items": [
    {
      "report_type": "invoice.v1",
      "name": "invoice",
      "category": "invoices",
      "description": "One row per Invoice Item for invoice reconciliation, with optional label, shipment, order-reference, insurance, refund, and address context.",
      "available_columns": [
        { "name": "invoice_object_id", "display_name": "invoice_object_id", "is_default": true },
        { "name": "invoice_number", "display_name": "invoice_number", "is_default": true },
        { "name": "invoice_total_amount", "display_name": "invoice_total_amount", "is_default": true }
      ],
      "data_availability": {
        "status": "ready",
        "start": 1704067200,
        "end": 1719360000,
        "updated": 1719360000
      }
    }
  ]
}
```

Two fields drive how you build a run:

* The `available_columns` field lists the column names you can request. Submit each column's `name`. Omit `columns` on create and you get the report's default set.
* The `data_availability` field tells you the window you can query. When `status` is `ready`, your interval must fall inside the `start` and `end` window, and `updated` shows when those bounds were last checked (UTC epoch seconds). When `status` is `pending`, a create request returns `404 data-unavailable`.

To inspect a single report type, call `GET /v2/reporting/reports/{report_type}`.

## 2. Create a run

A **run** is one async execution of a report type over a time interval. Set `interval` to `{start, end}` in epoch seconds.

* `end` must be strictly greater than `start`, or you get a `422`.
* A run can span up to 92 days. Going past a report type's limit returns `400 invalid-parameter`.

```shell theme={null}
curl https://api.goshippo.com/v2/reporting/runs \
  -H "Authorization: ShippoToken <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "report_type": "invoice.v1",
    "interval": { "start": 1704067200, "end": 1706745600 },
    "columns": ["invoice_object_id", "invoice_number", "invoice_total_amount"],
    "output_format": "csv_gzip",
    "idempotency_key": "invoices-jan-2024-001"
  }'
```

```json theme={null}
{
  "id": "3f1c9a7e-4d2b-4e8a-9c1f-2b6d8e0a5c11",
  "report_type": "invoice.v1",
  "interval": { "start": 1704067200, "end": 1706745600 },
  "columns": ["invoice_object_id", "invoice_number", "invoice_total_amount"],
  "output_format": "csv_gzip",
  "created": 1706745601,
  "status": "QUEUED"
}
```

You get `201 Created` with `status: QUEUED`. Keep the `id`; you need it to poll and download.

* `output_format` accepts `csv`, `csv_gzip` (the default), or `parquet`.
* `columns` is optional. If you include it, it can't be empty.
* Unknown body fields are rejected with `422`.

<Info>
  **Safe retries with `idempotency_key`.** This field is optional: a string of 1 to 128 characters that you choose. Send a new key, or none at all, and you get `201 Created` with a brand-new run. Retry with the same key and the same request body, and you get the original run back as `200 OK` instead of a duplicate. Reuse a key with a different body and you get a `409` with the `idempotency-conflict` type. Without a key, every create makes a new run, so set one whenever a retry might fire twice.
</Info>

## 3. Poll until the run finishes

```shell theme={null}
curl https://api.goshippo.com/v2/reporting/runs/3f1c9a7e-4d2b-4e8a-9c1f-2b6d8e0a5c11 \
  -H "Authorization: ShippoToken <API_TOKEN>"
```

A run moves through these states:

| `status`     | Terminal | Meaning                                    |
| ------------ | -------- | ------------------------------------------ |
| `QUEUED`     | No       | Queued; not started.                       |
| `PROCESSING` | No       | Generating the report.                     |
| `SUCCEEDED`  | Yes      | Done. `result.download` contains the URL.  |
| `FAILED`     | Yes      | Could not complete. `error.code` says why. |

Poll until the run reaches `SUCCEEDED` or `FAILED`.

<Warning>
  A run with `status: FAILED` comes back as **HTTP 200** with `status: FAILED`, not an HTTP error. Read `error.code` to handle it.
</Warning>

On failure, `error.code` is one of:

| `error.code`            | Meaning                                     |
| ----------------------- | ------------------------------------------- |
| `invalid_run_request`   | The run request is invalid.                 |
| `run_timeout`           | The run exceeded its processing time limit. |
| `run_processing_failed` | Data processing failed.                     |
| `run_failed`            | The run could not be completed.             |
| `service_unavailable`   | The service was temporarily unavailable.    |

## 4. Download the file

On `SUCCEEDED`, `result.download` takes one of two shapes:

* `{ "status": "available", "url": "...", "expires": <epoch_s> }` means the file is ready. Send a `GET` request to `url`; a Shippo `Authorization` header is not required.
* `{ "status": "unavailable" }` means a signed URL is not currently available. Fetch the run again to obtain a new URL.

```shell theme={null}
curl -o invoices.csv.gz \
  "https://<download-host>/<opaque-path>?<signature>"
```

<Info>
  The URL is time-limited. Read its `expires` field for the exact cutoff. Fetch the run again to obtain a new URL and expiry.
</Info>

## List your runs

```shell theme={null}
curl "https://api.goshippo.com/v2/reporting/runs?limit=20" \
  -H "Authorization: ShippoToken <API_TOKEN>"
```

Runs come back newest-first. `limit` defaults to 50 and ranges from 1 to 500. List responses don't include signed download URLs. GET a succeeded run by id to mint one.

## Error reference

Reporting API `4xx` and `5xx` responses use `application/problem+json`, except authentication failures, which use `401 application/json` with a `detail` field. Match problem responses on the last path segment of `type`, not its full URL.

| `type` segment          | HTTP | When                                                       |
| ----------------------- | ---- | ---------------------------------------------------------- |
| —                       | 401  | Token missing or unverified; handle by HTTP status.        |
| `invalid-column`        | 400  | A requested column is not in `available_columns`.          |
| `invalid-parameter`     | 400  | Bad interval or duplicate requested column.                |
| `report-type-not-found` | 404  | No such report type key.                                   |
| `data-unavailable`      | 404  | Interval outside the ready window.                         |
| `run-not-found`         | 404  | No run with that id.                                       |
| `idempotency-conflict`  | 409  | Reused an `idempotency_key` with a different request body. |
| `validation-error`      | 422  | Malformed body or unexpected field.                        |
| `service-unavailable`   | 503  | Temporary; retry.                                          |

## Next steps

<CardGroup cols={2}>
  <Card title="Reporting API reference" icon="book" href="/api-reference/reporting-api/overview">
    Full endpoint, schema, and field reference for every Reporting API call.
  </Card>

  <Card title="Authentication" icon="key" href="/guides/authentication">
    How to generate and use your Shippo API token.
  </Card>
</CardGroup>


## Related topics

- [API Quickstart Guide](/guides/api-quickstart.md)
- [Reporting API overview](/reporting-api/overview.md)
- [Migrate from the Invoice API](/reporting-api/migrate-from-invoice-api.md)
