API documentation
Public social media profile data as JSON, one schema for every platform. One key, 100 free requests a day for 14 days, and you only pay for what the API actually delivered.
1. Quickstart
From account to first response in 3 steps.
sk_live_ and is shown only once: copy it immediately./v1/ping is free and only confirms the key works.curl "https://api.sociallens.dev/v1/ping" \ -H "Authorization: Bearer sk_live_YOUR_KEY"
Expected response: {"ok":true,"user_id":123}. From there, swap /v1/ping for the endpoint you need.
curl "https://api.sociallens.dev/v1/profile?username=exemplo&platform=tiktok" \ -H "Authorization: Bearer sk_live_YOUR_KEY"
2. Authentication
Every call to /v1/* carries the key in the Authorization header, Bearer format. The key never goes in the URL, so it never leaks into proxy logs or browser history.
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Getting a key
Signed in, open /keys and generate a key with a name (e.g. "production", "staging"). You can have up to 10 active keys at once, one per environment or app. We store only the hash: a lost key cannot be recovered, only replaced.
Revoking
Same screen, "Revoke". The key stops working instantly and any call using it gets 401 invalid_token. Revoking does not affect your credits or your other keys.
A missing, wrong or revoked key always gets the same body:
{ "error": "invalid_token", "message": "API key missing, invalid or revoked." }3. Endpoints
Base URL: https://api.sociallens.dev. Every response is UTF-8 JSON. The schema is the same for all platforms: change platform and the fields stay the same.
Public profile
Reads a user's public profile and returns normalized counters and identity.
| Parameter | Type | Description |
|---|---|---|
usernamerequired | string | Public username. A leading @ is accepted and ignored. Letters, digits, dot, _ and -, up to 80 characters. |
platformrequired | string | One of: tiktok, instagram, youtube. |
Example request
curl "https://api.sociallens.dev/v1/profile?username=exemplo&platform=tiktok" \ -H "Authorization: Bearer sk_live_YOUR_KEY"
Example response 200
{
"username": "exemplo",
"platform": "tiktok",
"nickname": "Exemplo Oficial",
"verified": true,
"followers": 1284310,
"following": 212,
"likes": 48210933,
"videos": 431,
"bio": "Content creator. Contact in bio.",
"avatar": "https://.../avatar.jpg",
"fetched_at": "2026-11-07T12:02:15Z",
"credits": { "source": "paid", "charged": 1, "balance": 7999 }
}Response fields
| Field | Type | Meaning |
|---|---|---|
username | string | Username as the platform shows it, without @. |
platform | string | Platform queried, same as the parameter sent. |
nickname | string | null | Display name. null if the platform has none. |
verified | boolean | Verified by the platform. |
followers | integer | Followers (subscribers on YouTube). Always an integer, never "1.2M". |
following | integer | How many accounts the profile follows. 0 when the platform does not expose it. |
likes | integer | Total likes received (TikTok) or total views (YouTube). 0 when unavailable. |
videos | integer | Number of publications: videos, posts or uploads depending on the platform. |
bio | string | null | Bio text, unformatted. |
avatar | string | null | Profile picture URL. May expire; download it if you need to keep it. |
fetched_at | string | When the data was read, ISO 8601 UTC. We never serve cache without saying so. |
credits.source | string | paid (taken from paid balance) or trial (taken from the daily trial quota). |
credits.charged | integer | Credits charged for this call. |
credits.trial | object | Only when source is trial: used_today, limit_per_day, resets_at, days_left. |
credits.balance | integer | Your balance after this call, so you can monitor without an extra request. |
During the beta the response may include an extra informational note field. Ignore it in your parser; it goes away in the stable version.
Followers of a profile (paginated)
Lists a profile's public followers, one page per call. For the next page, repeat the call with the previous next_cursor. Each page costs 1 credit regardless of how many followers it contains.
| Parameter | Type | Description |
|---|---|---|
usernamerequired | string | Public username of the profile. @ accepted and ignored. |
platformrequired | string | One of: tiktok, instagram, youtube. |
cursoroptional | string | The next_cursor from the previous page. Omit on the first call. |
curl "https://api.sociallens.dev/v1/followers?username=exemplo&platform=tiktok" \ -H "Authorization: Bearer sk_live_YOUR_KEY"
{
"username": "exemplo",
"platform": "tiktok",
"followers": [
{ "username": "exemplo_um", "nickname": "Exemplo Um", "verified": false, "avatar": "https://..." },
{ "username": "exemplo_dois", "nickname": "Exemplo Dois", "verified": true, "avatar": "https://..." }
],
"next_cursor": "c2VndWludGU6MjA",
"fetched_at": "2026-11-07T12:02:15Z",
"credits": { "source": "paid", "charged": 1, "balance": 7998 }
}| Field | Type | Meaning |
|---|---|---|
username, platform | string | The profile queried, same as sent. |
followers[] | array | Followers on this page. Each item has username, nickname (string or null), verified (boolean) and avatar (URL or null). |
next_cursor | string | null | Pass it back as cursor for the next page. null when the list is over. |
fetched_at, credits | Same as /v1/profile. |
Fake follower estimate
Estimates the percentage of fake or bot followers of a profile from a sample of its followers. Costs more than a simple read because it analyzes several profiles per call. Ideal for vetting an influencer before a campaign, or for audit tools.
| Parameter | Type | Description |
|---|---|---|
usernamerequired | string | Public username of the profile. |
platformrequired | string | One of: tiktok, instagram, youtube. |
curl "https://api.sociallens.dev/v1/bot-estimate?username=exemplo&platform=tiktok" \ -H "Authorization: Bearer sk_live_YOUR_KEY"
{
"username": "exemplo",
"platform": "tiktok",
"bot_score": 23,
"classification": "low",
"sample_size": 200,
"fetched_at": "2026-11-07T12:02:15Z",
"credits": { "source": "paid", "charged": 5, "balance": 7993 }
}| Field | Type | Meaning |
|---|---|---|
bot_score | integer 0..100 | Estimated percentage of fake or bot followers. |
classification | string | low (below 30), medium (30 to 59) or high (60 or more). |
sample_size | integer | How many followers were analyzed to reach the score. |
fetched_at, credits | Same as /v1/profile. |
Ping (key check)
Consumes no credit and does not count toward the data rate limit. Returns {"ok":true,"user_id":N} when the key is valid.
Coming next
GET /v1/posts: latest posts of a profile with views, likes and comments.
4. Credits and cost
- Cost per endpoint, shown next to each one on this page and returned in
credits.charged:/v1/profile1,/v1/followers1 per page,/v1/bot-estimate5. On the trial, each call deducts its cost from the daily quota. - Free trial on sign-up: 100 requests a day for 14 days, no card. The daily counter resets at midnight UTC (
resets_atgives the exact time). Over the day: 402 daily_trial_limit. After the 14 days: 402 trial_expired. - Paid credits never expire and have no daily limit. When you have a paid balance it is used first; the trial only kicks in when the paid balance is zero. Packs: Starter US$ 19 = 8,000 · Pro US$ 79 = 40,000 · Scale US$ 299 = 180,000. Buy at /credits; details at /pricing.
- The
creditsblock of every response says where the request came from (source: "paid"or"trial") and, on the trial, how much of the day is used.
credits.charged.Every refund is recorded in your statement with the same reference as the charge, so it can be audited.
5. Rate limit
Each key is limited to 60 requests per minute on data endpoints. The limit is per key, not per account: two keys, two limits.
Over the limit, the API answers 429 rate_limited, charges nothing, and tells you how long to wait:
HTTP/1.1 429 Too Many Requests
Retry-After: 41
{
"error": "rate_limited",
"message": "Too many requests. Wait and try again.",
"retry_after": 41,
"limit_per_min": 60
}The Retry-After header and the retry_after field give the seconds until the window opens again. Sleep that long and retry; no exponential backoff needed. Need more volume? Reach us at /contact.
6. Errors
Every error has the same shape, always JSON: {"error": "<code>", "message": "..."}. The code is stable (use it in your switch); the message is for humans and may change.
| HTTP | error | When it happens | Charged? |
|---|---|---|---|
| 400 | bad_request | Missing or invalid parameter (empty username, platform not in the list) or unknown endpoint. | No |
| 401 | invalid_token | Authorization header missing, wrong or revoked key. | No |
| 402 | no_credits | Insufficient paid balance and no trial. Includes current balance. | No |
| 402 | daily_trial_limit | Hit the 100 requests of the day on the trial. Includes resets_at (midnight UTC). Buying a pack unblocks it immediately. | No |
| 402 | trial_expired | The 14 trial days are over and there is no paid balance. Upgrade at /credits. | No |
| 429 | rate_limited | More than 60 req/min on this key. Includes retry_after. | No |
| 502 | source_unavailable | The platform did not respond or blocked the read. Credit refunded. Retry in a few seconds. | No (refunded) |
Non-existent or private profiles are being defined together with the reading engine and will get their own code before the stable version. Until then they fall under source_unavailable, never charged.
7. Code examples
The same call in three languages. Replace sk_live_YOUR_KEY with your key. In production, read the key from an environment variable; do not hardcode it.
curl "https://api.sociallens.dev/v1/profile?username=exemplo&platform=tiktok" \ -H "Authorization: Bearer sk_live_YOUR_KEY"
import os, time, requests
API_KEY = os.environ.get("SOCIALDATA_KEY", "sk_live_YOUR_KEY")
BASE = "https://api.sociallens.dev"
def profile(username, platform="tiktok"):
r = requests.get(
f"{BASE}/v1/profile",
params={"username": username, "platform": platform},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=15,
)
if r.status_code == 429:
time.sleep(int(r.headers.get("Retry-After", 5)))
return profile(username, platform)
data = r.json()
if r.status_code != 200:
raise RuntimeError(f"{data['error']}: {data['message']}")
return data
p = profile("exemplo")
print(p["followers"], "followers · balance", p["credits"]["balance"])const API_KEY = process.env.SOCIALDATA_KEY ?? "sk_live_YOUR_KEY";
const BASE = "https://api.sociallens.dev";
async function profile(username, platform = "tiktok") {
const url = `${BASE}/v1/profile?` + new URLSearchParams({ username, platform });
const res = await fetch(url, { headers: { Authorization: `Bearer ${API_KEY}` } });
if (res.status === 429) {
const wait = Number(res.headers.get("Retry-After") ?? 5);
await new Promise(r => setTimeout(r, wait * 1000));
return profile(username, platform);
}
const data = await res.json();
if (!res.ok) throw new Error(`${data.error}: ${data.message}`);
return data;
}
const p = await profile("exemplo");
console.log(p.followers, "followers · balance", p.credits.balance);