Vercel Logo

How Do I See Exactly Where My Money Is Going?

A total tells you how much you spent. It does not tell you which model changed, which project got busy, or which request produced the charge.

AI Gateway records those dimensions as requests pass through it. We will seed a demo project, read the aggregate charts, and open one request record.

Quick Answer

Use the AI Gateway Usage view to compare requests, tokens, latency, and spend over time. The Requests view can narrow traffic by project or API key and show the cost of an individual request. Add request metadata when the application also needs feature or user attribution.

Outcome

Trace a spend change from a dashboard chart to one synthetic request and its model, project attribution, latency, and cost.

Fast Track

  1. Run pnpm seed-traffic from the demo app
  2. Open AI Gateway Usage and select the current-day range
  3. Open Requests, filter to the demo project, and inspect one row

Hands-on exercise

Use an isolated demo team or project. The traffic generator gives order chat most of the volume and sends smaller workloads to other models. This creates useful charts without exposing production prompts or customer traffic.

Run:

pnpm seed-traffic

The script logs each request as it lands, then prints a per-feature summary:

order-chat        openai/gpt-5.4-mini                35 sent  42,180 in / 3,904 out
menu-translation  google/gemini-3.5-flash-lite       15 sent  17,865 in / 1,410 out
daily-special     anthropic/claude-sonnet-4.6         8 sent   9,528 in / 1,102 out
...
Seeded 100 of 100 requests across 7 models.
Dashboards update within a few minutes; Custom Reporting rows can lag a little longer.

Pass a number for a smaller run with the same proportions: pnpm seed-traffic 25. On the free tier, a model outside the free subset fails its share of requests; the summary counts them, and the charts still fill in from the rest.

Try It

Wait for the requests to appear, then open AI Gateway in the dashboard.

In Usage, answer three questions:

  • Which model handled the most requests?
  • Which model has the highest time to first token?
  • Which model contributed the most spend during the selected range?

Next, open Requests, filter to the demo project, and choose one synthetic row. Confirm its timestamp, model, project or API-key attribution, and cost. The row identifies the request behind the aggregate chart.

Built-in versus custom attribution

Projects and API keys are useful workload boundaries. They cannot identify an end user or feature unless your application sends that metadata. Add user and tags when you need application-level attribution.

If the charts are empty, verify the terminal run succeeded and allow for dashboard ingestion. Do not switch to production traffic just to make the graph interesting.

Bookmark It

Bookmark the AI Gateway Usage and Requests views. They answer different questions, and production investigations usually move between both.

Done-When

  • The Usage view contains synthetic traffic for at least two models
  • You can explain one change in requests, latency, or spend from the chart
  • You opened one request and found its model, attribution, and cost
  • No production prompts, user IDs, or unrelated request records were exposed

Solution

The complete traffic generator lives at scripts/seed-traffic.ts. Its weighted mix sends most requests to inexpensive models and enough traffic to pricier models to make the Spend chart useful. The condensed version below shows the request plan; the full script adds jitter, small concurrent waves, a retry on 429, and the per-feature summary.

scripts/seed-traffic.ts
import { generateText } from "ai";
import { TRUCK_INSTRUCTIONS } from "../lib/menu";
 
const total = 100;
 
// Weights are per hundred requests. The full table has seven features;
// swap models freely, the dashboards chart whatever shows up.
const features = [
  { tag: "feature:order-chat", model: "openai/gpt-5.4-mini", weight: 35 },
  { tag: "feature:salsa-hotline", model: "deepseek/deepseek-v4-flash-0731", weight: 14 },
  { tag: "feature:daily-special", model: "anthropic/claude-sonnet-4.6", weight: 8 },
];
 
const plan = features.flatMap((feature) =>
  Array.from(
    { length: Math.round((feature.weight / 100) * total) },
    () => feature,
  ),
);
plan.sort(() => Math.random() - 0.5);
 
for (const feature of plan) {
  await generateText({
    model: feature.model,
    instructions: TRUCK_INSTRUCTIONS,
    prompt: "One synthetic taco question, in character.",
    providerOptions: {
      gateway: { user: "customer-107", tags: [feature.tag] },
    },
  });
}

Because the traffic is synthetic, every chart and request row is safe to inspect during the exercise.

Was this helpful?

supported.