SocialCrawl

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
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?

  1. Sign up at socialcrawl.dev. You get 100 welcome credits instantly.
  2. Open Dashboard → API Keys.
  3. Click Create Key and name it after where it runs (production, staging, local-dev).
  4. Copy the key from the one-time reveal dialog.

How do I send it?

cURL
curl "https://www.socialcrawl.dev/v1/tiktok/profile?handle=charlidamelio" \
  -H "x-api-key: $SOCIALCRAWL_KEY"
TypeScript
const res = await fetch(
  "https://www.socialcrawl.dev/v1/tiktok/profile?handle=charlidamelio",
  { headers: { "x-api-key": process.env.SOCIALCRAWL_KEY! } },
);
Python
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
curl "https://www.socialcrawl.dev/v1/tiktok/profile?handle=charlidamelio" \
  -H "Authorization: Bearer $SOCIALCRAWL_KEY"
You sendWhat happens
x-api-key onlyThe key is read from x-api-key
Authorization: Bearer <key> onlyThe key is read from the Bearer token. The word Bearer is not case-sensitive
Bothx-api-key wins. A wrong x-api-key is never rescued by the Bearer token
Authorization with no scheme, or Basic401 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.

CodeStatusWhat happenedFix
MISSING_API_KEY401No key reached us in x-api-key or as a Bearer tokenAdd the header. Check that your proxy is not eating it.
INVALID_API_KEY401The key is malformed, unknown, revoked, or expiredRe-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:

CodeStatusWhat happenedFix
INSUFFICIENT_CREDITS402The account balance is lower than the endpoint costTop up in Dashboard → Billing
KEY_BUDGET_EXCEEDED402This key spent its own credit limit. The account balance is fineRaise 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 agentFlowStart here
Has no browser, such as a CLI or a background jobAgent 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 redirectAuthorization 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:

ScopeAllows
socialcrawl:readFree account reads: /v1/credits/balance, /v1/keys/current and GET /v1/notifications
socialcrawl:spendEvery data call that can bill credits, and marking notifications read
socialcrawl:manageWith 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

RuleValue
Active keys per accountUp to 5
StorageSHA-256 hash for auth lookup, AES-256-GCM encrypted for retrieval
RevocationSoft delete. Revoked keys return 401 INVALID_API_KEY immediately
ExpiryOptional per-key expires_at (unset means never)
Credit limitOptional per-key spend cap (unset means unlimited)
Rate limitsApplied 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.

Example
Production key   no limit          <- your real traffic, unaffected
Test key         500 credit limit  <- stops at 500, whatever it does

Set 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-Remaining header: 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/current costs 0 credits and describes the key you call with: credit_limit, credits_used, credits_remaining, the account_balance, and spendable, 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.
Check the key you are using
curl https://www.socialcrawl.dev/v1/keys/current \
  -H "x-api-key: $SOCIALCRAWL_API_KEY"
Response (data)
{
  "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_at on any key you issue to a contractor or a short-lived job.
  • Call https://www.socialcrawl.dev exactly, and you can refuse redirects. No /v1 route 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 (without www) answers 301 to https://www.socialcrawl.dev, and any http:// URL answers 308 to https://. A client that never follows redirects therefore never sends its key to another host.

Next steps