---
title: API reference
description: Base URLs, authentication, request conventions, limits and retry guidance that apply to every Lovely Router endpoint.
---

Lovely Router serves two kinds of API from one host, with one key and one balance:

- **Model APIs** in the vendors' own formats: Anthropic Messages, OpenAI Chat Completions and Gemini. Requests and responses follow the vendor's API reference, so the official SDKs work unchanged. Lovely Router adds a few response headers.
- **Lovely Router APIs** for [data tools](/docs/tools) (`/v1/tools`) and the [browser agent](/docs/browser-agent) (`/v1/browser`): JSON APIs with the conventions described below.

:::cards
- :model: [Models endpoints](/docs/api/models) Messages, Chat Completions, Gemini, token counting and the model list.
- :tool: [Tools endpoints](/docs/api/tools) Search the catalog, run tools and fetch results.
- :browser: [Browser endpoints](/docs/api/browser) Start and steer browser agent runs, and manage secrets, files and profiles.
- :sparkle: [Music video endpoints](/docs/api/music-videos) Turn a song into a music video, review its scenes and download it.
- :shield: [Errors](/docs/api/errors) Status codes, error bodies and retry policy.
:::

## Base URLs

All endpoints are served over HTTPS from `https://lovelyrouter.com`.

| API | SDK base URL | Endpoints |
| --- | --- | --- |
| Anthropic Messages | `https://lovelyrouter.com` | `POST /v1/messages`, `POST /v1/messages/count_tokens` |
| OpenAI Chat Completions | `https://lovelyrouter.com/v1` | `POST /v1/chat/completions` |
| Gemini | `https://lovelyrouter.com` | `POST /v1beta/models/{model}:generateContent`, `:streamGenerateContent`, `:countTokens` |
| Model list | — | `GET /v1/models`, `GET /v1beta/models` |
| Data tools | `https://lovelyrouter.com/v1` | `/v1/tools/...` |
| Browser agent | `https://lovelyrouter.com/v1` | `/v1/browser/...` |
| Music videos | `https://lovelyrouter.com/v1` | `/v1/videos/music/...` |
| MCP server | — | `POST /mcp` |

Endpoints not listed here, such as OpenAI's Responses, Embeddings and Images APIs, return `404`.

## Authentication

Send your API key as a bearer token. The model endpoints also accept the vendors' own headers, so each SDK works with its defaults.

```http
Authorization: Bearer lr-...
```

| Header | Typical use |
| --- | --- |
| `Authorization: Bearer lr-...` | OpenAI SDK, data tools, browser agent, MCP |
| `x-api-key: lr-...` | Anthropic SDK |
| `x-goog-api-key: lr-...` | Google Gen AI SDK |

The model list and the tool catalog need no key. Everything else does. See [Authentication](/docs/authentication).

## Requests and responses

- **JSON in, JSON out.** Send request bodies as JSON with `content-type: application/json`. The exceptions are file uploads to the browser agent, which take the raw file or `multipart/form-data`, and run screenshots, which return `image/jpeg`.
- **Streaming.** Anthropic and OpenAI requests with `"stream": true` return server-sent events (`text/event-stream`). Gemini's `:streamGenerateContent` streams a JSON array, or server-sent events with `?alt=sse`, as Google's API does. See [Streaming](/docs/models/streaming).
- **Cross-origin requests** are allowed from any origin. That doesn't make it safe to call the API from a browser: anyone can read a key shipped in front-end code.

Lovely Router's own APIs follow these conventions:

| Convention | Example |
| --- | --- |
| Ids carry a type prefix | `run_…` for tool runs, `brun_…` for browser runs |
| Timestamps are ISO 8601 in UTC | `"created_at": "2026-10-11T09:30:00.000Z"` |
| Money is in US dollars, as numbers | `"charge_usd": 0.042`, `"reserved_usd": 0.3` |
| Lists are wrapped in an object | `{"object": "list", "data": [...]}` |
| Long-running work returns `202` | The body includes a `poll` path to check on it |
| Waiting is a query parameter | `GET /v1/tools/runs/{id}?wait=60` holds the request until the run finishes or the time is up |

## Response headers

Model responses carry these headers in addition to the vendor's:

| Header | Meaning |
| --- | --- |
| `x-lovely-request-id` | Unique id of the request, also shown in [Logs](/app/logs). Include it when you contact support. |
| `x-lovely-group` | The channel group that served the request |
| `x-lovely-channel` | A short, URL-encoded description of that channel group |
| `x-lovely-queued-ms` | Milliseconds the request waited for capacity, when it had to wait |
| `retry-after` | On `429` responses: seconds to wait before retrying |

## Versioning

**Model APIs follow the vendor's versioning.** Send the headers your SDK sends: `anthropic-version` and `anthropic-beta` for Anthropic, and `openai-beta` for OpenAI. Gemini requests use the `/v1beta` path, as Google's API does. Lovely Router forwards these headers and no others, so features that depend on a different vendor header aren't available.

**Lovely Router APIs are versioned by path** and live under `/v1`. New fields can appear in responses, so write clients that ignore fields they don't recognise.

## Rate limits

These limits apply to your requests:

| Limit | Applies to | When exceeded |
| --- | --- | --- |
| Requests per minute and concurrency you set on a key | Model requests from that key | `429` with `retry-after` |
| Model capacity | Model requests. Short bursts wait in a queue for a few seconds first. | `429`, usually with `retry-after` |
| 10 runs in progress per account | Data tool and browser runs | `429` |
| 100 requests per minute per key; 100 per minute shared by all callers without a key | `POST /v1/tools/suggest` | `429` with `retry-after` |

See [Rate limits & failover](/docs/models/rate-limits) and [API keys](/docs/account/api-keys).

## Retries and idempotency

Lovely Router retries failed model requests on other channels before it returns an error, so a `5xx` you receive has already been retried. It's still worth retrying `429` and `5xx` responses yourself with exponential backoff, honouring `retry-after`. Don't retry other `4xx` errors without changing the request. The official OpenAI and Anthropic SDKs retry twice by default; raise `max_retries` (`maxRetries` in Node.js) for batch jobs.

The API doesn't take idempotency keys. Plan for these cases:

- **A model request that times out on your side may still have completed** and been charged. If you retry, you may pay twice. Use generous client timeouts for long generations, or stream.
- **Starting a tool or browser run is not idempotent.** If `POST /v1/tools/{id}/run` or `POST /v1/browser/runs` times out, list your recent runs with `GET /v1/tools/runs` or `GET /v1/browser/runs` before starting another.
- **Reading is always safe to repeat.** Polling a run, listing runs and fetching results never charge.

Retry code in Python and Node.js is in [Retry failed requests](/docs/api/errors#retry-failed-requests).

## Errors

Model endpoints return errors in the vendor's format, so SDKs raise their usual exceptions. Lovely Router's own APIs return:

```json
{"error": {"type": "invalid_request_error", "message": "\"goal\" is required (max 4,000 characters)"}}
```

Every status code, with sample bodies for each format, is in [Errors](/docs/api/errors).
