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

# Date intervals

> Set a valid date interval when you create a Reporting API run, and use an idempotency key to retry safely.

Every Reporting API run needs a report type and a date interval. The interval picks which rows you export. This page covers how to set an interval the API accepts, how to choose output columns, and how idempotency keys work.

## Discover what a report type accepts

Read a report type's metadata before you build a create-run request. It tells you the columns the report type can output and the data window you can request.

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

Auth uses the ShippoToken header; see the [authentication guide](/guides/authentication).

This abbreviated response shows one available column:

```json theme={null}
{
  "report_type": "invoice.v1",
  "available_columns": [
    { "name": "invoice_object_id", "display_name": "invoice_object_id", "is_default": true }
  ],
  "data_availability": { "status": "ready", "start": 1701388800, "end": 1719360000, "updated": 1719363600 }
}
```

* `available_columns[]` lists the columns this report type can output. `is_default` marks the columns selected when you omit `columns` from a run.
* `data_availability` shows the data window you can request. `start` and `end` bound the window in UTC epoch seconds, and `updated` is when those bounds were last checked. A `status` of `pending` means the report type isn't runnable yet.

## Choose your columns

`columns` is optional. Leave it out and you get the columns where `is_default` is `true`. To choose the output explicitly, send a list of column `name` values from `available_columns[]`. The result keeps those columns in the order you submit.

A column name that is not listed in `available_columns` returns `400 invalid-column`.

## Set a date interval

`interval` is required. It's two integer UTC epoch **seconds**, not milliseconds and not ISO strings.

```json theme={null}
"interval": { "start": 1704067200, "end": 1706745600 }
```

* `end` has to be strictly greater than `start`. Equal or inverted bounds return `422` `validation-error`.
* The span can't exceed **92 days**. Over the limit returns `400` `invalid-parameter`, and the message gives you the exact cap. For a longer span, split it into multiple runs.
* The interval has to fall inside `data_availability`. Outside the ready window returns `404` `data-unavailable`.

## Create a run

```shell title="cURL" 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 }
  }'
```

```json theme={null}
{
  "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "report_type": "invoice.v1",
  "interval": { "start": 1704067200, "end": 1706745600 },
  "columns": ["invoice_object_id", "invoice_number", "invoice_total_amount"],
  "output_format": "csv_gzip",
  "status": "QUEUED",
  "created": 1706745601
}
```

A run starts as `QUEUED`, moves to `PROCESSING`, then ends as `SUCCEEDED` or `FAILED`. Poll `GET /v2/reporting/runs/{run_id}` until it reaches one of those terminal states. The returned download URL is time-limited; fetch the run again to obtain a new URL.

`output_format` is optional. Pick `csv`, `csv_gzip`, or `parquet`. Leave it out and you get `csv_gzip`.

## Retry safely with an idempotency key

`idempotency_key` is an optional string you supply, 1 to 128 characters. It lets you retry a create without risking a duplicate run.

| What you send                           | Result                                           |
| --------------------------------------- | ------------------------------------------------ |
| No key, or a new unused key             | `201 Created` and a fresh run (`status: QUEUED`) |
| The same key with the identical request | `200 OK` and the run you already created         |
| The same key with a different request   | `409` `idempotency-conflict`                     |

Without a key, every create makes a new run, so reuse the key if you want a retry to be safe. With the same key and the identical request, you can replay as many times as you need. On a `409`, pick a new key or send the original request.

## Errors

After authentication, Reporting API errors use `application/problem+json`. Match errors on the last path segment of `type`, not the full URI. Handle authentication failures by their `401` HTTP status.

| HTTP  | `type` ends with        | Cause                                                                    |
| ----- | ----------------------- | ------------------------------------------------------------------------ |
| `400` | `invalid-column`        | A column name is not listed in `available_columns`                       |
| `400` | `invalid-parameter`     | Interval exceeds the 92-day cap, or a column is requested more than once |
| `401` | —                       | Missing or invalid token; handle by HTTP status                          |
| `404` | `report-type-not-found` | Unknown `report_type`                                                    |
| `404` | `data-unavailable`      | Interval outside the report type's data window                           |
| `404` | `run-not-found`         | Unknown `run_id`                                                         |
| `409` | `idempotency-conflict`  | Reused `idempotency_key` with a different request body                   |
| `422` | `validation-error`      | `end` ≤ `start`, an unknown field, or a malformed body                   |
| `503` | `service-unavailable`   | Temporary outage; retry later                                            |

<Info>Match Reporting API errors on the last path segment of `type` (e.g. `invalid-parameter`), not the full URI. Authentication failures may instead be `401 application/json`; handle them by HTTP status.</Info>

A run can also fail after it is queued. That comes back as `200 OK` with `status: FAILED` and an `error` object whose code is one of `invalid_run_request`, `run_failed`, `run_processing_failed`, `run_timeout`, or `service_unavailable`.

## Next steps

<CardGroup cols={2}>
  <Card title="Create and poll a report run" icon="play" href="/reporting-api/create-and-poll">
    Walk the full async lifecycle from create through download.
  </Card>

  <Card title="Reporting API reference" icon="code" href="/api-reference/report-runs/create-report-run">
    Full schema, fields, and error responses.
  </Card>
</CardGroup>


## Related topics

- [Migrate from the Invoice API](/reporting-api/migrate-from-invoice-api.md)
- [Discover available reports](/reporting-api/discover-reports.md)
- [Create and poll a report run](/reporting-api/create-and-poll.md)
