Authentication¶
FirmaDB uses HTTP bearer tokens — never query parameters. Every authenticated request must include:
Key format¶
| Component | Values | Purpose |
|---|---|---|
env |
live or test |
Environment selector. Test keys hit live data but cap at 1,000 calls/day and don't bill. |
class |
sk or rk |
sk = secret key (full access). rk = restricted key (scoped). |
kid |
12 chars, base62 | Non-secret key ID — used for DB lookup, audit logs, and dashboard display. |
secret |
32 bytes, base62 | Random secret material. ≥190 bits of entropy. |
check |
6 chars, base62 | HMAC-SHA-256 checksum for fast format validation. |
Examples:
fdb_live_sk_aB12cD34eF56_7Hk2QmRn8wXz4PvY3LjB6Tab5mDf9xKc_a1B2c3
fdb_test_rk_xY99zZ00aA11_9Kp3RsUo7yWb2QxZ1NjA8VfC4mEh6tLg_d4E5f6
Two key classes¶
Secret keys (sk)¶
- Full access to every scope your tier permits.
- One per account. Use for server-to-server backends.
- Never expose them in browsers, mobile apps, or to AI agents. Use a restricted key instead.
Restricted keys (rk)¶
Multiple per account. Each rk is scoped, optionally constrained to specific IPs, countries, endpoints, and rate limits, and may have an expiry.
| Constraint | Type | Description |
|---|---|---|
scopes |
string[] |
Which operations the key may perform. See the scope table below. |
ip_allowlist |
string[] |
IP addresses or CIDR ranges. Empty = any IP. |
country_scope |
string[] |
ISO alpha-2 codes the key may access. Empty = all. |
endpoints |
string[] |
Path patterns (e.g. /v1/companies/*). Empty = all permitted by scopes. |
rate_limit_rpm |
integer |
Per-minute rate limit override. Lower of (this, tier default) wins. |
expires_at |
ISO datetime / null | When the key stops working. Default: 90 days for user keys, 365 for service keys. |
Use restricted keys for AI agents, partner integrations, edge workers, and anywhere a leak would otherwise compromise the account.
Scopes¶
| Scope | Permits |
|---|---|
companies:read |
GET /v1/companies/{country}/{registry_id} |
companies:search |
GET /v1/companies/search |
companies:enrich |
POST /v1/companies/lookup-batch |
coverage:read |
GET /v1/countries, GET /v1/countries/{code} |
usage:read |
GET /v1/account/usage |
Secret keys have all scopes implicitly. Restricted keys have only the scopes explicitly granted at creation.
When a restricted key attempts an operation outside its scope, the server returns 403 insufficient_scope with required_scopes, granted_scopes, and missing_scopes arrays — so the caller knows exactly what's missing.
Anonymous access¶
A small set of endpoints does not require a key. They're rate-limited per IP at 60 requests/hour:
| Endpoint | Purpose |
|---|---|
GET /v1/health |
System health and component status. |
GET /v1/countries |
List all supported countries with record counts. |
GET /v1/countries/{code} |
Per-country capability manifest. |
GET /llms.txt |
Agent discovery file (unlimited, static). |
GET /.well-known/agent-card.json |
A2A agent card (unlimited, static). |
GET /openapi.yaml |
OpenAPI spec (unlimited, static). |
Every other endpoint returns 401 unauthenticated without a valid Authorization header.
Public demo key¶
A read-only demo key is embedded in the docs code samples for unauthenticated visitors. It carries companies:read, companies:search, and coverage:read scopes, caps at 100 calls/day, and returns real data. Use it to try the API without signing up.
Test mode¶
Keys with env=test (fdb_test_*) hit the same live data but:
- Capped at 1,000 calls/day (resets at midnight UTC).
- Never bill. Useful for CI, integration tests, and documentation samples.
- Same scope and constraint rules as their
livecounterparts.
When the daily test cap is exhausted, the API returns 429 quota_exhausted with limit.bucket = "test_daily" and limit.reset_iso pointing to the next midnight UTC.
Example 401 response¶
A missing or malformed key returns:
{
"type": "https://errors.firmadb.com/unauthenticated",
"title": "Unauthenticated",
"status": 401,
"detail": "Missing Authorization header. Pass your API key as 'Authorization: Bearer fdb_...'",
"instance": "/v1/companies/FR/552120222",
"code": "unauthenticated",
"request_id": "req_01HZAB7MJX...",
"retryable": false,
"retry_after_seconds": null,
"correction": "Add the header: Authorization: Bearer <your_api_key>",
"suggested_request": null,
"documentation_url": "https://errors.firmadb.com/unauthenticated"
}
Response headers include WWW-Authenticate: Bearer realm="firmadb".
Key lifecycle¶
- Revocation. Revoked keys are rejected on the next request. In-flight requests authenticated before revocation complete normally.
- Expiry. Same behavior — checked once at request start.
- Rotation. Create a new restricted key, deploy it everywhere, then revoke the old one. There's no atomic rotation API at launch.
OAuth 2.1 (deferred)¶
OAuth 2.1 with PKCE and Dynamic Client Registration is on the roadmap for the hosted MCP server. Until it ships, MCP clients authenticate with restricted bearer keys (rk class). See MCP setup for how to configure this in Claude Desktop and other MCP-aware clients.