---
title: Custom Reporting
product: vercel
url: /docs/ai-gateway/capabilities/custom-reporting
type: conceptual
prerequisites:
  - /docs/ai-gateway/capabilities
  - /docs/ai-gateway
related:
  - /docs/ai-gateway/sdks-and-apis/openai-chat-completions
  - /docs/ai-gateway/sdks-and-apis/openai-chat-completions/advanced
  - /docs/ai-gateway/sdks-and-apis/responses
  - /docs/ai-gateway/sdks-and-apis/openresponses/provider-options
  - /docs/ai-gateway/sdks-and-apis/anthropic-messages-api
summary: Learn about custom reporting on Vercel.
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

# Custom Reporting

The Custom Reporting API gives you detailed visibility into your AI Gateway usage. You can break down costs and token consumption by model, user, tag, provider, or credential type to understand exactly where your AI spend is going.

Use it to:

- **Track costs by model**: See how much you're spending on each model and compare cost efficiency across providers
- **Monitor per-user usage**: Identify which users are driving the most spend and token consumption
- **Analyze by tags**: Tag requests by feature, environment, or team to attribute costs and track usage across your organization
- **Compare providers**: Understand cost and usage differences between providers serving the same models
- **Audit BYOK vs system credentials**: Break down usage by credential type to see the impact of bring-your-own-key requests

> **💡 Note:** Custom Reporting is in beta. The API is currently scoped to your entire account, so the API key you use will return usage data for everything on the account.

## Pricing

| Charge type | Cost                                                              |
| ----------- | ----------------------------------------------------------------- |
| Tag/User ID | $0.075 per 1,000 unique tag or user ID values written per request |
| Query       | $5 per 1,000 queries to the reporting endpoint                    |

## Applying user and tag info to requests

To use reporting, attach a `user` and/or `tags` to your AI Gateway requests. You can do this through the AI SDK, Chat Completions API, Responses API, OpenResponses API, or Anthropic Messages API.

### AI SDK

The AI SDK supports user and tag submission through the gateway provider. See the [AI SDK docs on usage tracking with user and tags](https://ai-sdk.dev/providers/ai-sdk-providers/ai-gateway#usage-tracking-with-user-and-tags) for details.

```typescript
import { generateText } from 'ai';

const { text } = await generateText({
  model: 'anthropic/claude-opus-4.6',
  prompt: 'Tell me about San Francisco.',
  providerOptions: {
    gateway: {
      user: 'user-123',
      tags: ['a', 'b'],
    },
  },
});
```

### Chat Completions API

You have two options when using the [Chat Completions API](/docs/ai-gateway/sdks-and-apis/openai-chat-completions):

1. **User only**: Pass `user` in the standard [chat completions `user` field](https://platform.openai.com/docs/api-reference/chat/create#chat_create-user)
2. **User and tags**: Pass `user` and/or `tags` through [provider options](/docs/ai-gateway/sdks-and-apis/openai-chat-completions/advanced#provider-options)

#### TypeScript

```typescript
const completion = await openai.chat.completions.create({
  model: 'anthropic/claude-sonnet-4.6',
  messages: [
    {
      role: 'user',
      content: 'Tell me about San Francisco.',
    },
  ],
  providerOptions: {
    gateway: {
      user: 'user-123',
      tags: ['a', 'b'],
    },
  },
});
```

#### Python

```python
completion = client.chat.completions.create(
    model='anthropic/claude-sonnet-4.6',
    messages=[
        {
            'role': 'user',
            'content': 'Tell me about San Francisco.',
        },
    ],
    extra_body={
        'providerOptions': {
            'gateway': {
                'user': 'user-123',
                'tags': ['a', 'b'],
            },
        },
    },
)
```

### Responses API

Pass `user` and/or `tags` through [provider options](/docs/ai-gateway/sdks-and-apis/responses) on the Responses API:

#### TypeScript

```typescript
const response = await openai.responses.create({
  model: 'anthropic/claude-sonnet-4.6',
  input: [
    {
      type: 'message',
      role: 'user',
      content: 'Tell me about San Francisco.',
    },
  ],
  providerOptions: {
    gateway: {
      user: 'user-123',
      tags: ['a', 'b'],
    },
  },
});
```

#### Python

```python
response = client.responses.create(
    model='anthropic/claude-sonnet-4.6',
    input=[
        {
            'type': 'message',
            'role': 'user',
            'content': 'Tell me about San Francisco.',
        },
    ],
    extra_body={
        'providerOptions': {
            'gateway': {
                'user': 'user-123',
                'tags': ['a', 'b'],
            },
        },
    },
)
```

### OpenResponses API

Pass `user` and/or `tags` through [provider options](/docs/ai-gateway/sdks-and-apis/openresponses/provider-options) on the OpenResponses API:

#### TypeScript

```typescript
const response = await fetch('https://ai-gateway.vercel.sh/v1/responses', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${process.env.AI_GATEWAY_API_KEY}`,
  },
  body: JSON.stringify({
    model: 'anthropic/claude-sonnet-4.6',
    input: [
      {
        type: 'message',
        role: 'user',
        content: 'Tell me about San Francisco.',
      },
    ],
    providerOptions: {
      gateway: {
        user: 'user-123',
        tags: ['a', 'b'],
      },
    },
  }),
});
```

#### Python

```python
response = client.responses.create(
    model='anthropic/claude-sonnet-4.6',
    input=[
        {
            'type': 'message',
            'role': 'user',
            'content': 'Tell me about San Francisco.',
        },
    ],
    extra_body={
        'providerOptions': {
            'gateway': {
                'user': 'user-123',
                'tags': ['a', 'b'],
            },
        },
    },
)
```

### Anthropic Messages API

Pass `user` and/or `tags` through [provider options](/docs/ai-gateway/sdks-and-apis/anthropic-messages-api) on the Anthropic Messages API:

#### TypeScript

```typescript
const message = await anthropic.messages.create({
  model: 'anthropic/claude-sonnet-4.6',
  max_tokens: 1024,
  messages: [
    {
      role: 'user',
      content: 'Tell me about San Francisco.',
    },
  ],
  providerOptions: {
    gateway: {
      user: 'user-123',
      tags: ['a', 'b'],
    },
  },
});
```

#### Python

```python
message = client.messages.create(
    model='anthropic/claude-sonnet-4.6',
    max_tokens=1024,
    messages=[
        {
            'role': 'user',
            'content': 'Tell me about San Francisco.',
        },
    ],
    extra_body={
        'providerOptions': {
            'gateway': {
                'user': 'user-123',
                'tags': ['a', 'b'],
            },
        },
    },
)
```

## Custom Reporting API reference

The reporting endpoint is available on Pro and Enterprise plans.

### Endpoint

```
GET https://ai-gateway.vercel.sh/v1/report
```

### Authentication

All requests require a Bearer token in the `Authorization` header:

```bash
Authorization: Bearer YOUR_API_KEY
```

### Required query parameters

| Parameter    | Type   | Description                       |
| ------------ | ------ | --------------------------------- |
| `start_date` | string | Start date in `YYYY-MM-DD` format |
| `end_date`   | string | End date in `YYYY-MM-DD` format   |

Dates are inclusive (both `start_date` and `end_date` are included) and in UTC.

### Optional query parameters

#### Grouping

| Parameter   | Type   | Options                                                                                                                             | Description                             |
| ----------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
| `group_by`  | string | `model`, `user`, `tag`, [`provider`](/docs/ai-gateway/models-and-providers/provider-options#available-providers), `credential_type` | How to group the results                |
| `date_part` | string | `hour`, `day`                                                                                                                       | Time period to group results into (UTC) |

#### Filtering

| Parameter         | Type   | Description                                                                                            | Example                          |
| ----------------- | ------ | ------------------------------------------------------------------------------------------------------ | -------------------------------- |
| `user_id`         | string | Filter by a specific user ID                                                                           | `user_123`                       |
| `model`           | string | Filter by a specific [model](https://vercel.com/ai-gateway/models) in `creator/model-name` format      | `anthropic/claude-sonnet-4.6`    |
| `tags`            | string | Filter by tags (comma-separated). Uses boolean OR, so any matching tag on a request counts as a match. | `production` or `production,api` |
| `credential_type` | string | Filter by credential type                                                                              | `byok` or `system`               |
| `provider`        | string | Filter by [provider](/docs/ai-gateway/models-and-providers/provider-options#available-providers)       | `openai`                         |

### Response format

The API returns a JSON object with a `results` array. Each result only contains the grouping field relevant to the `group_by` parameter you used. It can take a few minutes for requests to appear in the reporting endpoint.

```json
{
  "results": [
    {
      "day": "2026-01-01",
      "model": "anthropic/claude-sonnet-4.6",
      "provider": "anthropic",
      "user": "user_123",
      "tag": "production",
      "total_cost": 10.5,
      "market_cost": 12.0,
      "input_tokens": 1000,
      "output_tokens": 500,
      "cached_input_tokens": 200,
      "cache_creation_input_tokens": 50,
      "reasoning_tokens": 100,
      "request_count": 25
    }
  ]
}
```

### Response fields

| Field                         | Description                                                                           |
| ----------------------------- | ------------------------------------------------------------------------------------- |
| `day`                         | The date for this result group in UTC (`YYYY-MM-DD`)                                  |
| `model`                       | Present when `group_by=model`                                                         |
| `provider`                    | Present when `group_by=provider`                                                      |
| `user`                        | Present when `group_by=user`                                                          |
| `tag`                         | Present when `group_by=tag`                                                           |
| `total_cost`                  | Charged price in USD. Returns `0.00` for BYOK requests.                               |
| `market_cost`                 | Market price of the request at the time it ran. Includes both BYOK and non-BYOK cost. |
| `input_tokens`                | Input tokens used                                                                     |
| `output_tokens`               | Output tokens used                                                                    |
| `cached_input_tokens`         | Cached input tokens                                                                   |
| `cache_creation_input_tokens` | Cache creation tokens                                                                 |
| `reasoning_tokens`            | Reasoning tokens                                                                      |
| `request_count`               | Number of requests                                                                    |

All cost values are in USD and aggregated based on the grouping parameter.

## Querying reports with the AI SDK

Query spend reports with the AI SDK's `getSpendReport()` method. It accepts the same parameters as the REST API (in camelCase) and returns camelCase results.

```typescript
import { gateway } from 'ai';

const report = await gateway.getSpendReport({
  startDate: '2026-03-01',
  endDate: '2026-03-25',
  groupBy: 'model',
});

for (const row of report.results) {
  console.log(`${row.model}: $${row.totalCost.toFixed(4)}`);
}
```

You can combine tagging on requests with filtered queries to attribute costs by feature, team, or environment:

```typescript
import type { GatewayProviderOptions } from '@ai-sdk/gateway';
import { gateway, streamText } from 'ai';

// 1. Make requests with tags
const result = streamText({
  model: 'anthropic/claude-opus-4.6',
  prompt: 'Summarize this quarter's results',
  providerOptions: {
    gateway: {
      tags: ['team:finance', 'feature:summaries'],
    } satisfies GatewayProviderOptions,
  },
});

// 2. Later, query spend filtered by those tags
const report = await gateway.getSpendReport({
  startDate: '2026-03-01',
  endDate: '2026-03-31',
  groupBy: 'tag',
  tags: ['team:finance'],
});

for (const row of report.results) {
  console.log(
    `${row.tag}: $${row.totalCost.toFixed(4)} (${row.requestCount} requests)`,
  );
}
```

See the [AI SDK docs on spend reports](https://ai-sdk.dev/providers/ai-sdk-providers/ai-gateway#querying-spend-reports) for the full list of parameters and response fields.

## Generation lookup

Use the AI SDK's `getGenerationInfo()` method to look up a specific generation by its ID, including cost, token usage, latency, and provider details. Generation IDs are available in `providerMetadata.gateway.generationId` on both `generateText` and `streamText` responses.

When streaming, the generation ID is injected on the first content chunk, so you can capture it early without waiting for completion. This is useful when a network interruption cuts off the final response — the gateway records the final status server-side, so you can use the generation ID to look up the results later.

#### generateText

```typescript
import { gateway, generateText } from 'ai';

const result = await generateText({
  model: 'anthropic/claude-opus-4.6',
  prompt: 'Explain quantum entanglement briefly',
});

const generationId = result.providerMetadata?.gateway?.generationId;
const generation = await gateway.getGenerationInfo({ id: generationId });

console.log(`Model: ${generation.model}`);
console.log(`Cost: $${generation.totalCost.toFixed(6)}`);
console.log(`Latency: ${generation.latency}ms`);
console.log(`Prompt tokens: ${generation.promptTokens}`);
console.log(`Completion tokens: ${generation.completionTokens}`);
```

#### streamText

```typescript
import { gateway, streamText } from 'ai';

const result = streamText({
  model: 'anthropic/claude-opus-4.6',
  prompt: 'Explain quantum entanglement briefly',
});

let generationId: string | undefined;

for await (const part of result.fullStream) {
  if (!generationId && part.providerMetadata?.gateway?.generationId) {
    generationId = part.providerMetadata.gateway.generationId as string;
  }
}

if (generationId) {
  const generation = await gateway.getGenerationInfo({ id: generationId });
  console.log(`Cost: $${generation.totalCost.toFixed(6)}`);
  console.log(`Finish reason: ${generation.finishReason}`);
}
```

See the [AI SDK docs on generation lookup](https://ai-sdk.dev/providers/ai-sdk-providers/ai-gateway#generation-lookup) for the full list of response fields.

## REST API usage examples

### Group by day

```bash
curl "https://ai-gateway.vercel.sh/v1/report?start_date=2026-01-01&end_date=2026-01-31&date_part=day" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Group by model per hour

```bash
curl "https://ai-gateway.vercel.sh/v1/report?start_date=2026-01-01&end_date=2026-01-31&date_part=hour&group_by=model" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Group by user

```bash
curl "https://ai-gateway.vercel.sh/v1/report?start_date=2026-01-01&end_date=2026-01-31&group_by=user" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Group by tag

```bash
curl "https://ai-gateway.vercel.sh/v1/report?start_date=2026-01-01&end_date=2026-01-31&group_by=tag" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Group by credential type

```bash
curl "https://ai-gateway.vercel.sh/v1/report?start_date=2026-01-01&end_date=2026-01-31&group_by=credential_type" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Filter by user, model, or tags

You can combine filters to narrow results:

```bash
curl "https://ai-gateway.vercel.sh/v1/report?start_date=2026-01-01&end_date=2026-01-31&date_part=day&user_id=user_123&model=anthropic/claude-sonnet-4.6&tags=production,api" \
  -H "Authorization: Bearer YOUR_API_KEY"
```


---

[View full sitemap](/docs/sitemap)
