Authentication
API key format, scopes, brand restriction, team keys, and key rotation.
Every request to the Public API authenticates with an opaque bearer token in
the Authorization header. Browser CORS calls are blocked at the edge, so the
API is server-to-server only.
The header
curl https://api.rankprompt.com/v1/me \
-H "Authorization: Bearer rp_live_YOUR_KEY"const res = await fetch('https://api.rankprompt.com/v1/me', {
headers: { Authorization: `Bearer ${process.env.RANKPROMPT_API_KEY}` },
});
const me = await res.json();import os, httpx
res = httpx.get(
"https://api.rankprompt.com/v1/me",
headers={"Authorization": f"Bearer {os.environ['RANKPROMPT_API_KEY']}"},
timeout=30,
)
res.raise_for_status()
me = res.json()Keys are prefixed with rp_live_ so they are easy to detect in logs and pull
requests. We submit the prefix to GitHub secret scanning so leaks are caught
automatically.
Scopes
Scopes are granular per resource. The implication hierarchy is:
* ⊃ read:all / write:all ⊃ specific scopes; write:foo ⊃ read:foo.
| Scope | Grants |
|---|---|
read:all | Read access to every resource type. |
write:all | Read + write access to every resource type. |
read:brands | List and inspect brands. |
write:brands | Create, update, and delete brands (implies read). |
read:reports | List and inspect reports. |
write:reports | Create, update, and delete reports (implies read). |
write:prompts | Create, update, and delete prompts (implies read). |
read:jobs | List and inspect jobs. |
write:jobs | Create, update, and delete jobs (implies read). |
read:region-configs | List and inspect region-configs. |
write:region-configs | Create, update, and delete region-configs (implies read). |
read:scheduled | List and inspect scheduled. |
write:scheduled | Create, update, and delete scheduled (implies read). |
read:citations | List and inspect citations. |
write:citations | Create, update, and delete citations (implies read). |
write:audit | Create, update, and delete audit (implies read). |
read:webhooks | List and inspect webhooks. |
write:webhooks | Create, update, and delete webhooks (implies read). |
read:meta | List and inspect meta. |
read:competitors | List and inspect competitors. |
write:competitors | Create, update, and delete competitors (implies read). |
read:content | List and inspect content. |
write:content | Create, update, and delete content (implies read). |
read:cms | List and inspect cms. |
write:cms | Create, update, and delete cms (implies read). |
read:brand-audits | List and inspect brand-audits. |
write:brand-audits | Create, update, and delete brand-audits (implies read). |
read:tasks | List and inspect tasks. |
write:tasks | Create, update, and delete tasks (implies read). |
read:analytics | List and inspect analytics. |
read:ads | List and inspect ads. |
read:seo | List and inspect seo. |
write:seo | Create, update, and delete seo (implies read). |
read:outreach | List and inspect outreach. |
write:outreach | Create, update, and delete outreach (implies read). |
read:community | List and inspect community. |
write:community | Create, update, and delete community (implies read). |
read:gbp | List and inspect gbp. |
write:gbp | Create, update, and delete gbp (implies read). |
read:client-reports | List and inspect client-reports. |
write:client-reports | Create, update, and delete client-reports (implies read). |
read:agency | List and inspect agency. |
write:prospects | Create, update, and delete prospects (implies read). |
write:billing | Create, update, and delete billing (implies read). |
read:meta and GET /v1/me
GET /v1/me is the introspection endpoint that confirms your credentials
work. It always returns user_id, auth_type, plan, account_id,
account_role and account_plan (see Team keys). The fields
that describe what your key can do (key_prefix, scopes,
allowed_brand_id) are only included when the key carries the
read:meta scope.
When a request lands with insufficient scope you get back the
insufficient_scope error from the error catalog: a 403
whose details carry both the missing scope (required_scope) and the
scopes your key actually has (key_scopes):
{
"error": {
"code": "insufficient_scope",
"message": "This endpoint requires the 'write:brands' scope",
"details": {
"required_scope": "write:brands",
"key_scopes": ["read:brands"]
}
},
"request_id": "01HZK3..."
}
Brand restriction
Optionally bind a key to a single brand at creation time
(allowed_brand_id). The API will then refuse any request whose target brand
does not match, even if the calling key carries write:all. This lets you
issue per-brand keys to clients without having to spin up a new Rank Prompt
workspace.
A request that crosses the boundary fails with brand_not_authorized (403).
Team keys
A key acts in the account that was active when it was created: your own, or
a team account you hold a seat in, with your role there. GET /v1/me says
which one:
| Field | What it tells you |
|---|---|
account_id | The account the key acts in. Equal to user_id for a key made in your own account. |
account_role | Your role in that account: owner in your own, editor or viewer in a team account. |
account_plan | That account’s plan, which plan-gated account features and the monthly request pool follow. plan stays your own plan. |
What changes for a key made in a team account:
- Losing the seat stops the key. If you are no longer an active member
of that account, every call answers
403 account_membership_required, with the account indetails.account_id. The key never falls back to your own account. - A failed membership read is retryable. If the membership cannot be
checked, the call answers
503 service_unavailablewith aRetry-Afterheader. Wait that many seconds and send the same request again. - Billing stays with the owner. Buying credits
(
POST /v1/me/credits/purchases) and starting a subscription (POST /v1/me/subscriptions) answer403 forbidden: that account’s billing belongs to its owner. Use a key made in your own account, withwrite:billing, for both. - The request pool is the account’s. Every key made in that account,
the owner’s and the team’s, draws on one monthly pool sized by the
account’s plan. When it runs out the
429carries noupgrade_url: only the owner raises it. A key you make in your own account keeps your own pool. - The owner sees and revokes team keys. The account owner lists every
key made in their account, with the request units each drew this month,
and can revoke one, with a reason, without removing you from the team.
GET /v1/me/usage?account_id=counts only the calls made in one account (see Inspecting your usage). Leaving the team or being removed revokes your keys for that account. - Work on a brand is charged to the brand’s owner. A report, an audit or an article on a brand draws on its owner’s credits, whichever account the key acts in.
Lifecycle: create, rotate, revoke
Manage keys from the Developers page inside your workspace. Common operations:
- Create: pick scopes, optional brand restriction, and optional expiry. The full key is shown once at creation time and never again.
- Rotate: the key’s Rotate action issues a new key with the same name, scopes, brand restriction, expiry and account, shows it once, and revokes the old one at that moment. The monthly request count carries over. For zero downtime, create a second key instead, deploy it, then revoke the old one.
- Revoke: disables the key immediately. In-flight requests still complete;
the next call returns
invalid_api_key. - Expire: keys with an
expires_atstart returningexpired_api_keyonce the timestamp passes. Use this for short-lived integrations.
Storage hygiene
- Treat keys like database passwords. Never commit them, never log them, never put them in client-side code.
- Prefer your platform’s secret manager (AWS Secrets Manager, GCP Secret Manager, Vercel/Netlify env vars, Doppler, 1Password CLI).
- Rotate immediately on suspected exposure; we cannot revoke retroactively what has already been read from your codebase.