API keys
Every call to /api/v2/* is authenticated with an API key. Keys are tied to your Professional subscription. This page covers how to send them, how to rotate them, and what to do if one leaks.
How to authenticate
Two header formats are accepted. Use whichever suits your tooling:
Option A: Authorization: Bearer (recommended)
This is the standard for most API tooling (Postman, curl, Python requests, fetch, axios, language SDKs). If you're not sure which to pick, use Bearer.
curl -H "Authorization: Bearer brubru_live_abc123..." \
"https://brubru.beresol.eu/api/v2/legislative/eur-lex/laws?limit=5"
Option B: X-API-Key header
A bit easier to type by hand in curl one-liners. Interchangeable with Bearer.
curl -H "X-API-Key: brubru_live_abc123..." \
"https://brubru.beresol.eu/api/v2/legislative/eur-lex/laws?limit=5"
If you send both, Bearer wins. The X-API-Key header is ignored in that case. So it's safe to leave one configured at the SDK level and occasionally override with the other.
Key format
Brubru API keys look like:
brubru_live_0123456789abcdef0123456789abcdef0123456789abcdef
That's the literal prefix brubru_live_ followed by 48 hexadecimal characters (192 bits of entropy). The prefix helps us and GitHub's secret-scanner find leaked keys automatically.
Getting a key
- Subscribe to the Professional plan at brubru.beresol.eu/subscription. API access is included.
- Email hello@beresol.eu with your Professional account email. We'll mint the key and deliver it over a secure channel (Signal, encrypted email).
- Self-serve key generation (mint/rotate/revoke from your account page) ships with multi-tenant organisations in May 2026. For now it's manual.
Rotation & revocation
To rotate a key (recommended at least once every 90 days, immediately after an incident):
- Email hello@beresol.eu requesting a new key. We mint a new one and keep the old one active for 24 hours so you can roll forward without downtime.
- Update your secrets store (Railway variables, GitHub Actions secrets, AWS Secrets Manager, etc.) with the new key.
- Deploy. Validate with a ping.
- Reply to confirm: we then revoke the old key.
If you believe a key has leaked, email us immediately. We revoke within an hour. Keys are stored as SHA-256 hashes on our side; we never see your plaintext after issuing.
Tier & access checks
At each request we verify:
- The key exists and is not revoked.
- The owner's
subscription_tierequalsblue(Professional). A downgrade stops the key working immediately. - The owner's account is active.
- The per-key rate limit has not been exceeded in the current 60-second window.
Any failure returns a structured 401/403/429: see Errors.
Storing the key safely
Never commit API keys to git. Use environment variables, secret managers, or platform-provided secret stores.
Good patterns:
# In .env (add .env to .gitignore)
BRUBRU_API_KEY=brubru_live_...
# In your code
import os
import httpx
r = httpx.get(
"https://brubru.beresol.eu/api/v2/legislative/eur-lex/laws",
headers={"Authorization": f"Bearer {os.environ['BRUBRU_API_KEY']}"},
)
If you absolutely must inline the key in client-side JavaScript (don't: proxy through your backend), scope it to read-only and rotate aggressively.
WWW-Authenticate challenge
401 responses include the WWW-Authenticate header:
WWW-Authenticate: Bearer realm="Brubru API"
This lets HTTP client libraries surface a friendly auth prompt (curl with --anyauth, browser fetch with credential prompts, etc.).
Examples
Python
import os
import httpx
BASE = "https://brubru.beresol.eu"
headers = {"Authorization": f"Bearer {os.environ['BRUBRU_API_KEY']}"}
r = httpx.get(f"{BASE}/api/v2/legislative/eur-lex/laws", params={"q": "GDPR", "limit": 3}, headers=headers)
r.raise_for_status()
print(r.json()["data"])
Node.js
const response = await fetch(
"https://brubru.beresol.eu/api/v2/legislative/eur-lex/laws?q=GDPR&limit=3",
{ headers: { Authorization: `Bearer ${process.env.BRUBRU_API_KEY}` } }
);
const body = await response.json();
console.log(body.data);
TypeScript (axios)
import axios from "axios";
const api = axios.create({
baseURL: "https://brubru.beresol.eu",
headers: { Authorization: `Bearer ${process.env.BRUBRU_API_KEY}` },
});
const { data } = await api.get("/api/v2/legislative/eur-lex/laws", { params: { q: "GDPR", limit: 3 } });
Next
Once you can make a 200 call, head to Response envelope to understand the shape every endpoint returns.