Validate API Key
Confirm credentials and inspect the caller's access scope, teams, and rate-limit state.
curl --request GET \
--url https://api.aiclicks.io/api/v1/validate \
--header 'Authorization: Bearer ak_live_xxx'
import httpx, os
resp = httpx.get(
"https://api.aiclicks.io/api/v1/validate",
headers={"Authorization": f"Bearer {os.environ['AICLICKS_API_KEY']}"},
)
resp.raise_for_status()
payload = resp.json()["data"]
print("key_type:", payload["key_type"])
print("teams:", [t["name"] for t in payload["teams"]])
print("rate_limit:", payload["rate_limit"])
const resp = await fetch("https://api.aiclicks.io/api/v1/validate", {
headers: { Authorization: `Bearer ${process.env.AICLICKS_API_KEY}` },
});
if (!resp.ok) throw new Error(`validate failed: ${resp.status}`);
const { data } = await resp.json();
console.log(data.key_type, data.teams, data.rate_limit);
{
"data": {
"valid": true,
"key_type": "api_key",
"key": {
"id": "8f1d3c0a-2f9b-4c11-9b80-7a82e1f0c3f3",
"name": "Production exports",
"created_at": "2026-02-14T10:11:02.318Z"
},
"user_id": "f1b9e2d4-3a4b-4f1d-9b9c-12b9c0e5d2a4",
"teams": [
{
"id": "1be5a7d4-0c8b-4f1d-9b9c-12b9c0e5d2a4",
"name": "Acme",
"developer_access": true,
"domains_count": 3
}
],
"allowed_domains": [
{
"id": "21c1f9aa-9b6d-4e3a-8a31-2b0c0e1f2a3b",
"name": "Acme Inc.",
"website": "acme.com"
},
{
"id": "32d2e8bb-aa7e-5f4b-9b42-3c1d1f2a3b4c",
"name": "Acme Labs",
"website": "labs.acme.com"
}
],
"rate_limit": {
"limit_per_minute": 60,
"remaining": 58,
"reset_at": 1718531412
}
},
"generated_at": "2026-06-16T08:00:11.218Z"
}
{
"data": {
"valid": true,
"key_type": "jwt",
"key": null,
"user_id": "f1b9e2d4-3a4b-4f1d-9b9c-12b9c0e5d2a4",
"teams": [
{ "id": "...", "name": "Acme", "developer_access": true, "domains_count": 3 },
{ "id": "...", "name": "Globex","developer_access": false, "domains_count": 0 }
],
"allowed_domains": null,
"rate_limit": {
"limit_per_minute": 60,
"remaining": null,
"reset_at": null
}
},
"generated_at": "2026-06-16T08:00:11.218Z"
}
{
"detail": "Invalid or revoked API key"
}
{
"detail": "Too many requests. Please try again later."
}
Use this endpoint to verify that an API key (or JWT) is live, see which teams and domains it can reach, and read the current rate-limit window in one round trip. It is intentionally cheap and never cached — call it on startup, on key rotation, or whenever a downstream 401/403 needs explanation.
For an API-key caller, the response is scoped to the key's team. For a JWT caller, it spans every team the user belongs to. Teams without Developer Access appear in the teams list (so you can surface them in UI), but their domains_count is forced to 0.
Authorizations
Your API key formatted as Bearer ak_live_<your-key>, or a Bearer JWT from the dashboard session. Create keys in the dashboard under Settings → Developers.
Optional UUID for log correlation. If omitted, we generate one and echo it back in the response.
Query parameters
This endpoint takes no query parameters.
Response
The validation payload.
Always true on a 200. Authentication failures return 401 with detail instead — there is no valid: false body.
One of api_key or jwt. Useful for surfacing the auth method back in dashboards or audit logs.
Present only for api_key callers. null for JWT.
UUID of the API key record.
Human-readable label set when the key was created.
ISO-8601 timestamp the key was minted.
UUID of the user that owns this key (or the JWT subject).
Every team this caller is a member of (scoped to the key's team for api_key callers).
Team UUID.
Team display name.
Whether this team has the public API enabled. When false, no domain endpoints will return data for this team.
Number of domains the caller can access on this team after applying allowed_domains. Forced to 0 when developer_access is false.
Flat list of domains the caller can access across all teams. null means unrestricted — the caller can access every domain on every developer-enabled team. An empty array means the caller has access to zero domains.
Domain UUID. Use this as {domain_id} in every other endpoint.
Display name.
Bare hostname, e.g. acme.com.
Current rate-limit state for this caller. remaining and reset_at are null for JWT callers (rate limiting only applies to API keys).
Max requests per minute allowed for this key. Default is 60.
Requests remaining in the current window. null for JWT callers.
Unix epoch seconds when the window resets. null for JWT callers.
ISO-8601 timestamp of when the server produced this response.
Response headers
| Header | Description |
|---|---|
X-Cache | Always BYPASS — this endpoint never caches. |
X-Request-Id | Unique request id. Echoes incoming if you set one. |
X-RateLimit-Limit | Max requests per minute for this key. |
X-RateLimit-Remaining | Requests remaining in current minute. |
X-RateLimit-Reset | Unix epoch seconds when the window resets. |
Caching
This endpoint is never cached. Every call reads the key, the team list, and the current rate-limit bucket live — use it freely on startup and on every key rotation.
Errors
Missing, malformed, or revoked API key. The response body is { "detail": "..." }; there is no valid: false envelope.
Rate limit exceeded. Inspect the Retry-After header for how long to wait, then call again.
Notes
- A
200withdeveloper_access: falseon every team andallowed_domains: []means the key is structurally valid but cannot read any data. Surface this to the user as "API access not enabled — contact support@aiclicks.io" rather than as an auth failure. - For JWT callers,
rate_limit.remainingandrate_limit.reset_atarenullbecause rate limiting is keyed off theak_token, not the session. allowed_domainsis the union across teams post-allowlist. If you need the per-team breakdown, callGET /api/v1/domains— it returns the same domains with their team association.