Tags & attribution
Tags are key=value labels you attach to a call — which customer it served, which feature sent it, which environment. Belarel records them with the call, returns them on GET /v1/requests/{id}, and lets you aggregate usage by any of them. Tags are attribution only: they never change what a key is allowed to do.
Three ways to send tags
Section titled “Three ways to send tags”| Source | Example |
|---|---|
X-Belarel-Tags header |
X-Belarel-Tags: customer=acme,feature=support |
metadata in the body (the OpenAI field, on Chat Completions and Responses) |
"metadata": { "customer": "acme" } |
belarel_tags in the body |
"belarel_tags": { "customer": "acme" } |
The header works with any client, including ones that cannot add body fields. metadata is the field the OpenAI SDKs already type, so it needs no cast.
curl https://api.belarel.com/v1/chat/completions \ -H "Authorization: Bearer $BELAREL_API_KEY" \ -H "Content-Type: application/json" \ -H "X-Belarel-Tags: customer=acme,feature=support" \ -d '{"model": "<model-id>", "messages": [{"role": "user", "content": "Hello"}]}'import osfrom openai import OpenAI
client = OpenAI( base_url="https://api.belarel.com/v1", api_key=os.environ["BELAREL_API_KEY"], default_headers={"X-Belarel-Tags": "feature=support"}, # every call)completion = client.chat.completions.create( model="<model-id>", messages=[{"role": "user", "content": "Hello"}], metadata={"customer": "acme"}, # this call)import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.belarel.com/v1", apiKey: process.env.BELAREL_API_KEY, defaultHeaders: { "X-Belarel-Tags": "feature=support" }, // every call});const completion = await client.chat.completions.create({ model: "<model-id>", messages: [{ role: "user", content: "Hello" }], metadata: { customer: "acme" }, // this call});Syntax and limits
Section titled “Syntax and limits”| Rule | Limit |
|---|---|
| Header format | Comma-separated key=value pairs; spaces around each pair are trimmed. |
| Body format | An object whose values are all strings. |
| Keys | Lowercased, then 1–40 characters of a-z, 0-9, _, ., -, starting with a letter or digit. |
| Values | Strings of at most 200 characters. |
| Count | At most 16 distinct tags that you send per call — header, metadata and belarel_tags combined. The key’s default tags are added on top and do not count. |
A malformed tag refuses the call before anything is spent:
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
invalid_tags |
400 | A pair without =, a body value that is not a string, an invalid key, a value over 200 characters, or more than 16 tags. The message names the problem. |
Fix the tag and retry. |
missing_tags |
400 | The key requires a tag the model call did not carry. The message lists the missing names. | Send the tag, or ask for a default on the key. |
Both errors carry param: "belarel_tags", whichever source held the tag.
Precedence
Section titled “Precedence”When the same tag name arrives from several places, the most specific one wins:
belarel_tagsin the bodymetadatain the body- the
X-Belarel-Tagsheader - the application headers (
X-Title,HTTP-Referer, below) - the key’s default tags
Application headers
Section titled “Application headers”Following the OpenRouter convention, a client application can name itself:
X-Title: Acme Supportbecomes the tagapp=Acme Support(cut to 200 characters).HTTP-Referer: https://support.acme.com/inboxbecomesapp_url=https://support.acme.com— the origin only.
These two never cause a refusal. They are added only if you did not set app or app_url yourself, and only if the call has room under the 16-tag limit.
Required and default tags on a key
Section titled “Required and default tags on a key”A key can carry required tags — names every model call must include — and default tags, applied when the call does not set them. A default satisfies a requirement. Both are visible on GET /v1/key as required_tags and default_tags.
A sub-key always requires at least its parent’s required tags, and inherits the parent’s default tags under its own. A reseller whose key requires customer therefore gets that tag on every sub-key’s traffic.
Report usage by tag
Section titled “Report usage by tag”Group your aggregates by any tag name with group_by=tag:<name>:
curl "https://api.belarel.com/v1/usage?group_by=tag:customer" \ -H "Authorization: Bearer $BELAREL_API_KEY"Each row’s group is a tag value; calls without that tag are grouped under null. The aggregate covers the key and its sub-keys. See Cost & usage.

