Skip to content

Authentication

Every endpoint except GET /v1/health requires an API key. REST and MCP use the same keys.

The bearer header

Send the key in the Authorization header on every request:

Authorization: Bearer trru_YOUR_KEY
curl -H "Authorization: Bearer $TRRU_KEY" "https://trru.atapinar.com/v1/latest?ids=ru.key_rate"
import os
import requests

resp = requests.get(
    "https://trru.atapinar.com/v1/latest",
    params={"ids": "ru.key_rate"},
    headers={"Authorization": f"Bearer {os.environ['TRRU_KEY']}"},
    timeout=30,
)
print(resp.status_code)
const resp = await fetch("https://trru.atapinar.com/v1/latest?ids=ru.key_rate", {
  headers: { Authorization: `Bearer ${process.env.TRRU_KEY}` },
});
console.log(resp.status);

The scheme name Bearer is case-insensitive. There is no query-string or cookie alternative: a key in a URL is not accepted.

Scopes

Scope Grants
data All read endpoints under /v1 and all MCP tools except the admin ones
admin Everything in data, plus: company contact details in company cards, and the raw read-only SQL tool in MCP (query_sql)

A key needs the data scope to be accepted at all. Keys are created with an explicit scope list, for example data or data,admin. Give integrations a data key; admin is for the operator's own tooling.

On /mcp, the server picks its tool set from the key: an admin key sees query_sql and company contact fields, any other key does not. The MCP instructions the server sends to the client match the key's scope.

401 and 403

Status Meaning Typical cause Fix
401 unauthorized No usable key Header missing, scheme not Bearer, key mistyped, key revoked Check the header; ask the operator for a new key if it was revoked
403 forbidden The key is valid but lacks the data scope A key created without data Ask the operator for a key with the data scope

Both responses use the standard error shape (Errors).

A request with no key, a real response:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
X-Request-Id: <request id>
{"error": {"code": "unauthorized", "message": "missing or invalid API key"}}

A wrong or revoked key returns exactly the same body. The service does not tell you which of "missing", "wrong" and "revoked" applies.

A key without the data scope gets (message text as the service sends it):

{"error": {"code": "forbidden", "message": "this key lacks the 'data' scope"}}

Note

403 is about the scope, not about the endpoint: with a data key you will not get a 403 from any REST route. The admin extras are not separate routes. They change what GET /v1/companies/{inn} returns and which tools /mcp offers, so a data key never sees an "access denied" for them; the fields and the tool are simply absent.

Key handling

  • Keep the key in an environment variable or a secrets manager (TRRU_KEY in these docs). Do not hard-code it in source files or commit it.
  • Never put the key in client-side code (web pages, mobile apps that ship the key, browser extensions). Anyone who can open the page can read it. The API sends no CORS headers on purpose; call it from a server (FAQ).
  • Do not paste keys into chat tools, tickets or logs. If your HTTP client logs request headers, mask Authorization.
  • One key per integration. When something misbehaves or a key leaks, the operator can revoke that one key without touching the others.
  • Keys are shown once when created and stored hashed on the server. They cannot be recovered. A lost key is replaced, not retrieved.
  • Rotation: ask the operator for a new key, switch your integration over, then ask for the old key to be revoked. Both keys work in the meantime.
  • Each key has its own rate limit (Rate limits).

Without a key

Only GET /v1/health works without a key:

curl https://trru.atapinar.com/v1/health

Unknown routes also answer with the standard 404 body, with or without a key. Calling a data route without a key returns 401, not 404.