Authentication
How to create, use, cap, and rotate SocialCrawl API keys, sent in the x-api-key header or as a Bearer token
Every request carries your API key in the x-api-key header, or as Authorization: Bearer <key> if your tool prefers that. The key is the entire credential: no token exchange and no session. An AI agent that cannot hold a key can sign in with OAuth instead.
Checking your account…
curl "https://www.socialcrawl.dev/v1/tiktok/profile?handle=charlidamelio" \
-H "x-api-key: YOUR_API_KEY"What does a key look like?
sc_ followed by 32 random bytes, base64url-encoded. That makes every key 46 characters long.
The full key is shown exactly once, in the dialog that appears when you create it. After that the dashboard shows only the last 4 characters. You can reveal it again yourself from the dashboard after a fresh sign-in, but support never sees the plaintext, so copy it into a secret manager before you close the dialog.
How do I create one?
- Sign up at socialcrawl.dev. You get 100 welcome credits instantly.
- Open Dashboard → API Keys.
- Click Create Key and name it after where it runs (
production,staging,local-dev). - Copy the key from the one-time reveal dialog.
How do I send it?
curl "https://www.socialcrawl.dev/v1/tiktok/profile?handle=charlidamelio" \
-H "x-api-key: $SOCIALCRAWL_KEY"const res = await fetch(
"https://www.socialcrawl.dev/v1/tiktok/profile?handle=charlidamelio",
{ headers: { "x-api-key": process.env.SOCIALCRAWL_KEY! } },
);import os
import requests
res = requests.get(
"https://www.socialcrawl.dev/v1/tiktok/profile",
params={"handle": "charlidamelio"},
headers={"x-api-key": os.environ["SOCIALCRAWL_KEY"]},
)Can I send it as a Bearer token?
Yes. Most agents, MCP clients and HTTP tools (Postman, n8n, Make, OpenAPI generators) reach for Authorization: Bearer first, and /v1 reads the same key from there. It is the same key under the same rules: same format check, same credit limit, same rate limits, same billing.
curl "https://www.socialcrawl.dev/v1/tiktok/profile?handle=charlidamelio" \
-H "Authorization: Bearer $SOCIALCRAWL_KEY"| You send | What happens |
|---|---|
x-api-key only | The key is read from x-api-key |
Authorization: Bearer <key> only | The key is read from the Bearer token. The word Bearer is not case-sensitive |
| Both | x-api-key wins. A wrong x-api-key is never rescued by the Bearer token |
Authorization with no scheme, or Basic | 401 MISSING_API_KEY, and the message tells you to add the Bearer scheme |
The OpenAPI spec at /v1/openapi.json declares both as alternative security schemes (ApiKeyAuth and BearerAuth), so a generated client can use either.
Never put the key in a URL or query string. It ends up in browser history, proxy logs, and referrer headers.
What does a 401 mean?
Two codes, and they need different fixes.
| Code | Status | What happened | Fix |
|---|---|---|---|
MISSING_API_KEY | 401 | No key reached us in x-api-key or as a Bearer token | Add the header. Check that your proxy is not eating it. |
INVALID_API_KEY | 401 | The key is malformed, unknown, revoked, or expired | Re-check the key in Dashboard → API Keys |
Neither is retryable, and neither costs credits.
An API key is never refused with a 403: only an OAuth access token can get one, as INSUFFICIENT_SCOPE. The two failures you might expect as a 403 are both 402, because both are spend conditions rather than permission conditions:
| Code | Status | What happened | Fix |
|---|---|---|---|
INSUFFICIENT_CREDITS | 402 | The account balance is lower than the endpoint cost | Top up in Dashboard → Billing |
KEY_BUDGET_EXCEEDED | 402 | This key spent its own credit limit. The account balance is fine | Raise or reset the key's limit |
They need opposite responses, so branch on error.type rather than on the status. Topping up an account that was never short will not unblock a capped key.
OAuth for agents
An API key is the simplest credential. Use OAuth when an AI agent acts for someone and should not hold their key. The person signs in and approves the agent, and the agent gets its own access token, which you send as Authorization: Bearer <token>.
Each approved agent has its own connection key behind its tokens. Its spend is tracked on its own, you can cap it, and you can disconnect it in Dashboard → Connected apps without touching your personal keys.
Your agent has two ways to get a token:
| The agent | Flow | Start here |
|---|---|---|
| Has no browser, such as a CLI or a background job | Agent registration: it shows you a link and a 6-digit code, and you enter the code after signing in | /auth.md |
| Can open a browser and receive a redirect | Authorization code with PKCE, with resource=https://www.socialcrawl.dev | /.well-known/oauth-authorization-server |
Agents can discover all of this from a 401, whose WWW-Authenticate header links to /.well-known/oauth-protected-resource.
A token carries scopes, and /v1 checks them before anything is billed:
| Scope | Allows |
|---|---|
socialcrawl:read | Free account reads: /v1/credits/balance, /v1/keys/current and GET /v1/notifications |
socialcrawl:spend | Every data call that can bill credits, and marking notifications read |
socialcrawl:manage | With socialcrawl:spend: monitors, cohorts, web jobs, listening, webhooks and schedules |
A token is bound to one service, so a token issued for the hosted MCP server is refused on /v1, and the reverse. Tokens expire, and the agent renews them with its refresh token. An API key is never scope-checked.
Key management
| Rule | Value |
|---|---|
| Active keys per account | Up to 5 |
| Storage | SHA-256 hash for auth lookup, AES-256-GCM encrypted for retrieval |
| Revocation | Soft delete. Revoked keys return 401 INVALID_API_KEY immediately |
| Expiry | Optional per-key expires_at (unset means never) |
| Credit limit | Optional per-key spend cap (unset means unlimited) |
| Rate limits | Applied per key, not per account. See Rate limits |
How do I rotate a key?
Create the new key, deploy it, confirm traffic has moved, then revoke the old one. Revocation takes effect on the very next request, so revoking first means downtime.
If a key has leaked, revoke it immediately and accept the downtime. A live key is billable by anyone holding it.
Per-key credit limits
Every key on your account draws on the same credit balance. That is usually what you want, until a test script loops, a CI job misfires, or a new integration is pointed at the wrong endpoint, and the balance your production traffic depends on is gone.
A credit limit caps how many credits one key may ever spend. Set one on a key, and that key stops at the cap while every other key on the account keeps working normally.
Production key no limit <- your real traffic, unaffected
Test key 500 credit limit <- stops at 500, whatever it doesSet it in Dashboard → API Keys. When you create a key, tick Set a credit limit for this key and enter a number. Leave the box unticked for no limit, which is the default and what a production key normally wants. You can add, change, or remove a limit on an existing key at any time from its ⋯ menu.
Once a capped key reaches its limit, its requests return 402 KEY_BUDGET_EXCEEDED and nothing is deducted. Your account balance is untouched, so the rest of your integration keeps running.
Changes take effect on the key's very next request. There is no cache to wait out.
Read a key's limit from the API
A script or agent does not need the dashboard to see where a key stands.
- On every response. A key with a credit limit gets an
X-Key-Credits-Remainingheader: the credits it may still spend after that call, never below 0. A key with no limit gets no header. Stop, or switch keys, before it reaches 0. - On demand.
GET /v1/keys/currentcosts 0 credits and describes the key you call with:credit_limit,credits_used,credits_remaining, theaccount_balance, andspendable, the smaller of the remaining limit and the balance, which is what the key can actually spend right now. It also gives the key's last 30 days of requests and credits.
curl https://www.socialcrawl.dev/v1/keys/current \
-H "x-api-key: $SOCIALCRAWL_API_KEY"{
"name": "client-acme",
"key_prefix": "sc_live_ab",
"credit_limit": 500,
"credits_used": 120,
"credits_remaining": 380,
"account_balance": 10000,
"spendable": 380,
"last_30_days": {
"from": "2026-09-25",
"to": "2026-10-24",
"requests": 40,
"credits_used": 300
}
}A key with no limit reads null for credit_limit, credits_used and credits_remaining, and its spendable is the account balance. To bill clients per key, or to see which endpoints each key spends on, use GET /v1/credits/usage with group_by=api_key,endpoint.
Security guidance
- Never ship a key in client-side code. Browser and mobile bundles are readable, and every request made with your key is billed to your account. Put a server route in front of the API and keep the key on the server.
- Use a separate key per environment. Revoking a staging key should not take down production.
- Cap every non-production key. A CI or test key with a credit limit cannot drain the balance production runs on, however badly it misbehaves.
- Store keys in a secret manager, not in git. Encrypted environment variables, AWS Secrets Manager, and 1Password all work.
- Set an
expires_aton any key you issue to a contractor or a short-lived job. - Call
https://www.socialcrawl.devexactly, and you can refuse redirects. No/v1route answers with a 3xx on that host; a path with a trailing slash or a different case answers 404. Only the host itself redirects:https://socialcrawl.dev(withoutwww) answers 301 tohttps://www.socialcrawl.dev, and anyhttp://URL answers 308 tohttps://. A client that never follows redirects therefore never sends its key to another host.
