---
title: Usage & logs
description: Track spend, tokens, latency and errors across your keys, inspect any request, and reconcile what you were charged.
---

The dashboard records every model request, tool run and browser run. Requests show up within a few seconds of finishing.

| Page | What it shows |
| --- | --- |
| [Overview](/app) | Spend, reliability and latency over time, by channel group and by model |
| [Logs](/app/logs) | Every model request with its tokens, charge and timing |
| [Tools](/app/tools) | Every data tool and browser agent run with its results and charge |
| [Browser agent](/app/browser) → **History** | Your recent browser runs, with their steps and answers |
| [API keys](/app/keys) | Lifetime spend and last use of each key |
| [Billing](/app/billing) | Balance, credits and daily spend |

## Overview dashboard

[Overview](/app) summarises your traffic across all keys for the last 24 hours, 7, 30 or 90 days.

| Metric | Meaning |
| --- | --- |
| **Spend** | Total charged in the period, with the number of requests |
| **Success rate** | Share of requests that succeeded, with the number that failed |
| **P95 TTFT** | 95th percentile time to first token, with the average |
| **Throughput** | Average output tokens per second |
| **Cache hit rate** | Share of input tokens read from the prompt cache |
| **Balance** | Current balance, with your number of active keys |

Below the totals are charts of spend and tokens over time, a breakdown **By channel group** and **By model**, and an **Error distribution** of failed requests by error type. Time to first token and throughput are measured on streaming requests.

Data tool and browser agent runs count toward spend. They appear in the `tools` group, and in **By model** as `tool:` followed by the tool id, for example `tool:browser-agent`.

## Logs

[Logs](/app/logs) lists your model requests, newest first, 50 at a time. Filter by channel group, model, API key (when you have more than one) or status (**Succeeded** or **Failed**), and click **Load more** to go further back.

| Column | Meaning |
| --- | --- |
| **Time** | When the request started. Hover for the exact time. |
| **Model** | The model id. `⇢` marks a streaming request. |
| **Group** | The channel group that served it |
| **Status** | HTTP status returned to you |
| **In** | Input tokens, including tokens written to the prompt cache |
| **Cached** | Input tokens read from the prompt cache |
| **Out** | Output tokens, including reasoning tokens |
| **TTFT** | Time to first token (streaming requests) |
| **Tok/s** | Output tokens per second |
| **Charge** | What the request cost you |

Click a row for its details: **Request id**, the **Key** that made it, the **API format** (`anthropic`, `openai` or `gemini`), total **Latency**, **Cache write** and **Reasoning tokens**, **Cache hit** rate, the **Error** type for a failed request, and **Affinity**. The details end with **Ask support about this request**, which opens a support request with its id filled in.

| Affinity | Meaning |
| --- | --- |
| `new` | First request of a conversation |
| `sticky` | Later turn that stayed on its conversation's channel, so its prompt cache could be reused |
| `explicit` | Kept together by a session id your client sent |
| `none` | No conversation to keep together |

See [Prompt caching](/docs/models/caching) for why affinity matters for cost.

### What appears in Logs

- Every model request that passed the checks on your key and balance, successful or not. Failed requests show their status and error type and are charged $0.
- Requests refused up front are not logged and never charged: a missing or invalid key, a body that isn't JSON, an unknown model, a model outside the key's allow-list, an empty balance, or a key over its rate limit. Your code receives the error directly.

Logs and tool run history are kept for 90 days.

## Request ids

Every model response carries an `x-lovely-request-id` header, such as `req_3f9c1a7e5b2d4c6e8a0b1d2f`. It's the **Request id** shown in Logs. Every error carries one too. A request refused up front (bad key, empty balance, unknown model) isn't in Logs, but support can still trace it from its id.

Log the id with your own records, and include it when you contact support about a specific call.

```bash title="cURL"
curl -sD - -o /dev/null https://lovelyrouter.com/v1/chat/completions \
  -H "Authorization: Bearer $LOVELY_API_KEY" \
  -H "content-type: application/json" \
  -d '{"model": "gpt-6.1-sol", "messages": [{"role": "user", "content": "Hi"}]}' \
  | grep -i '^x-lovely'
```

```python title="Python"
# client = OpenAI(base_url="https://lovelyrouter.com/v1", api_key=os.environ["LOVELY_API_KEY"])
raw = client.chat.completions.with_raw_response.create(
    model="gpt-6.1-sol",
    messages=[{"role": "user", "content": "Hi"}],
)
print(raw.headers.get("x-lovely-request-id"))
completion = raw.parse()
```

```js title="Node.js"
// const client = new OpenAI({ baseURL: 'https://lovelyrouter.com/v1', apiKey: process.env.LOVELY_API_KEY });
const { data: completion, response } = await client.chat.completions
  .create({ model: 'gpt-6.1-sol', messages: [{ role: 'user', content: 'Hi' }] })
  .withResponse();
console.log(response.headers.get('x-lovely-request-id'));
```

Responses also carry `x-lovely-group`, the channel group that served the request. Tool runs and browser runs are identified by their run id (`run_…` or `brun_…`) instead.

## Tool runs

[Tools](/app/tools) shows your runs, results and spend over the last 30 days, then every run with its tool, status (**succeeded**, **running**, **failed**, **timed out** or **stopped**), results delivered out of the maximum, charge and run id. A running run shows the amount reserved for it instead of a charge. Browser agent runs are listed here too.

The same list is available from the API:

```bash title="cURL"
curl "https://lovelyrouter.com/v1/tools/runs?limit=20" \
  -H "Authorization: Bearer $LOVELY_API_KEY"
```

Each run has `charge_usd` (`null` until it settles) and `reserved_usd` (the hold while it runs). See [Run a tool](/docs/tools/run).

## Browser runs

The **History** tab on [Browser agent](/app/browser) lists your last 50 runs with their status, goal, number of steps and start time. Click one to see its steps, answer and extracted data. From the API, call `GET /v1/browser/runs`, or `GET /v1/browser/runs/{id}` for one run with its charge. See [Browser agent](/docs/browser-agent).

## Reconcile your charges

Your balance moves for two reasons: credits add to it, usage takes from it.

- **Credits**: top-ups, redeemed codes, referral earnings and free credit, listed under **Credit history** on [Billing](/app/billing).
- **Usage**: model requests in [Logs](/app/logs), plus tool and browser runs in [Tools](/app/tools).
- **Holds**: runs still in progress hold their maximum cost until they settle, then release what they didn't use.

To check one key, compare its **Spent** column on [API keys](/app/keys), which covers model requests, tool runs and browser runs, with its requests in Logs.

A few things that explain small differences:

- **Settling takes seconds.** Charges are recorded within a few seconds of a request finishing, so totals taken during heavy traffic can lag slightly.
- **Cancelled streams are billed for what was processed.** If you close a stream before its final usage event, output is billed from an estimate of the text already sent to you. See [Streaming](/docs/models/streaming#cancel-a-stream).
- **The Charge column is authoritative.** It is the amount deducted from your balance for that request. Prices per token are on the [model list](/models).

## Contact support {#contact-support}

Open [Support](/app/support) in the dashboard and click **New request**. Add the request id (`req_…`) or run id (`run_…`, `brun_…`) when your question is about a specific call; from a row in [Logs](/app/logs), **Ask support about this request** fills it in for you. Replies appear on the same page, and the **Support** item in the sidebar shows a badge when there is a new one. Reply on the request to add details, or mark it resolved.
