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

# Report

> Your daily sent impressions, billable impressions, and spend: parameters, response, and errors.

One read-only endpoint returns your DSP's delivery and spend for a time window, one row per UTC day plus a total. It covers your own DSP only. The key decides whose numbers you get.

Spend is in **US dollars**, as a decimal string (`"161.46358"`). Parse it as a decimal, not a float.

## Authorizations

**Authorization** `string` *required*

Your DSP reporting key as a Bearer token: `Authorization: Bearer dk_...`. Thrad issues it; ask your Thrad contact or email [contact@thrad.ai](mailto:contact@thrad.ai). It is not the token you gave Thrad for bid requests. A missing header returns `401 auth_required`. An unknown or revoked key returns `401 invalid_api_key`.

## Query parameters

**start** `string` *required*

Start of the window, inclusive. An ISO 8601 timestamp with a timezone, on the hour in UTC, for example `2026-09-01T00:00:00Z`. It must be within the last 365 days.

**end** `string` *required*

End of the window, exclusive. Same format as `start`, after it, at most 366 days later, and no more than one day in the future.

```bash cURL theme={null}
curl -G https://api.thrad.ai/v1/dsp/report \
  -H "Authorization: Bearer $THRAD_DSP_KEY" \
  --data-urlencode 'start=2026-09-01T00:00:00Z' \
  --data-urlencode 'end=2026-09-03T00:00:00Z'
```

```python Python theme={null}
import os
from decimal import Decimal

import requests

response = requests.get(
    "https://api.thrad.ai/v1/dsp/report",
    headers={"Authorization": f"Bearer {os.environ['THRAD_DSP_KEY']}"},
    params={"start": "2026-09-01T00:00:00Z", "end": "2026-09-03T00:00:00Z"},
    timeout=30,
)
response.raise_for_status()
report = response.json()
for day in report["data"]:
    print(day["date"], day["billable_impressions"], Decimal(day["spend"]))
```

```json 200 - Daily rows theme={null}
{
  "dsp": "medianet",
  "start": "2026-09-01T00:00:00+00:00",
  "end": "2026-09-03T00:00:00+00:00",
  "currency": "USD",
  "data": [
    {
      "date": "2026-09-01",
      "sent_impressions": 103508,
      "billable_impressions": 66427,
      "spend": "161.46358"
    },
    {
      "date": "2026-09-02",
      "sent_impressions": 114045,
      "billable_impressions": 71578,
      "spend": "233.6391"
    }
  ],
  "total": {
    "sent_impressions": 217553,
    "billable_impressions": 138005,
    "spend": "395.10268"
  }
}
```

```json 400 - Window not on the hour theme={null}
{
  "error": {
    "message": "`start` must be on the hour (UTC).",
    "type": "invalid_request_error",
    "param": "start",
    "code": "invalid_range"
  }
}
```

```json 401 - Unknown key theme={null}
{
  "error": {
    "message": "Invalid API key.",
    "type": "authentication_error",
    "param": null,
    "code": "invalid_api_key"
  }
}
```

```json 429 - Too fast theme={null}
{
  "error": {
    "message": "Request was throttled. Expected available in 1 second.",
    "type": "rate_limit_error",
    "param": null,
    "code": "rate_limit_exceeded"
  }
}
```

## Response

**dsp** `string`

Your DSP's name at Thrad.

**start** `string`

The window start you asked for, in UTC.

**end** `string`

The window end you asked for, in UTC. Exclusive.

**currency** `string`

Always `"USD"`.

**data** `array`

One row per UTC day that had impressions, oldest first. Days with none are left out.

**row properties**

**date** `string`

The UTC day, `YYYY-MM-DD`.

**sent\_impressions** `integer`

Your ads Thrad served to a publisher that day, whether or not they were seen.

**billable\_impressions** `integer`

Of those, the ones that became viewable (at least 50% on screen for at least one second). On OpenRTB, Thrad fires your `burl` for each one.

**spend** `string`

The total of your winning bids on billable impressions, in US dollars. Each impression adds its CPM bid divided by 1,000.

**total** `object`

The sum of every row in `data`, with the same three fields. `"0"` spend and zero counts when the window had no impressions.

Today's row is still filling in. An impression that is never viewed is counted about 30 to 60 minutes after it was served, on the day it was served, so a day is final about an hour after it ends in UTC.

## Errors

Errors use the shape `{ "error": { "message", "type", "param", "code" } }`. When one parameter is at fault, `param` names it.

| Status | Type | Code | When |
| - | - | - | - |
| `400` | `invalid_request_error` | `invalid_range` | `start` or `end` is missing, malformed, has no timezone, or is not on the hour; `end` is not after `start`; the window is longer than 366 days; `start` is more than 365 days ago; or `end` is more than a day in the future. |
| `401` | `authentication_error` | `auth_required` | No `Authorization` header was sent. |
| `401` | `authentication_error` | `invalid_api_key` | The key is malformed, unknown, or revoked. |
| `429` | `rate_limit_error` | `rate_limit_exceeded` | More than one request in a second. Wait for the `Retry-After` header. |
| `504` | `server_error` | `clickhouse_timeout` | The report took too long to compute. Retry, or ask for a shorter window. A timeout never returns zeros. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.