Enrichment API

Find the email address, profile, and social footprint for any Instagram, TikTok or YouTube creator from a single handle. One request returns the result, and you only pay a credit when an email is found.

REST · JSONBearer authSynchronousPay per email found

Base URL

All requests are made over HTTPS.

base url
https://enrichment.socialgems.io

Agents and code generators can read this reference as an OpenAPI 3.1 spec or as llms.txt.

Authentication

Every request requires an API key sent as a Bearer token in the Authorization header. Keys are tied to a customer and a tier, which determines rate limits.

header
Authorization: Bearer YOUR_API_KEY
Never expose keys client-side. Call the API from your server or a trusted backend. Treat keys like passwords — rotate them if they leak.

Quickstart

Send a handle and read the result from the response. New accounts start with 10 free credits, and repeat requests for the same creator are free. Creators we've already looked up come back in under a second, but the first lookup of a new creator can take up to about 90 seconds, so set your client timeout to at least 2 minutes.

Try it

POST /api/v1/enrichuses your API key
New accounts get 10 free credits to try it.
// The live response appears here

From your code

curl (instagram)
curl -X POST https://enrichment.socialgems.io/api/v1/enrich \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "handle": "zendaya"
  }'
curl (tiktok)
curl -X POST https://enrichment.socialgems.io/api/v1/enrich \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "handle": "khaby.lame",
    "platform": "tiktok"
  }'
curl (youtube)
curl -X POST https://enrichment.socialgems.io/api/v1/enrich \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "handle": "https://www.youtube.com/@mattdavella",
    "platform": "youtube"
  }'
javascript · fetch
const res = await fetch("https://enrichment.socialgems.io/api/v1/enrich", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    handle: "zendaya",
    platform: "instagram",                  // optional; defaults to "instagram"
  }),
})

if (res.status === 402) {
  // Out of credits — the body's buyUrl sells the next pack.
}
if (res.status === 503) {
  // Upstream briefly unavailable — retry after the Retry-After header.
}

const { status, result, credits } = await res.json()
// status: "completed" (email found) or "partial" (no email found)
// credits: { charged: 1, remaining: 259 } — only a found email costs a credit
const primaryEmail = result.emails[0]?.email

Pricing

You pay in credits, and only for results. One credit unlocks one email for a creator you haven't looked up before.

Free
$0

10 credits when your account is created.

Credit pack
$29250 credits

$0.12 per email found. Credits land as soon as the payment clears.

RequestCost
Email found for a creator you haven't unlocked1 credit
The same creator again, any time laterFree
No email found (status: "partial")Free
Upstream error (503)Free

Every response says what it cost in credits.charged and what is left in credits.remaining. Out of credits, requests for new creators return 402 insufficient_credits with a buyUrl, and nothing is charged. Creators you've already unlocked keep working.


Enrich a creator

Look up the email and profile for an Instagram, TikTok or YouTube handle.

POST/api/v1/enrich

Request body

ParameterTypeDescription
handlerequiredstringCreator handle with or without the leading @. Normalized to lowercase. 1–30 characters, alphanumeric plus . and _. YouTube handles are 3–30 characters and may also contain -; you can pass the channel URL instead (https://www.youtube.com/@name). Channel ids (UC…) aren't supported.
platformoptionalenum"instagram", "tiktok" or "youtube". Defaults to "instagram". Results are cached independently per platform, so the same handle can be enriched on each without collisions.
forceRefreshadmin onlybooleanBypass the result cache and look the creator up again. Ignored unless the API key is on the admin tier.

Example request

request · json
{
  "handle": "zendaya",
  "platform": "instagram"
}

Responses

StatusCondition
200The result. status is completed when an email was found and partial when not. credits says what the call cost. Cache hits carry cached: true.
402Out of credits for a creator you haven't unlocked. No credit was spent. See Pricing.
4xxValidation, auth, or rate-limit error. See Errors.
503The upstream data source is briefly unavailable. Nothing was cached — retry after the Retry-After header.
200 ok · email found
{
  "handle": "zendaya",
  "platform": "instagram",
  "customerId": "acct_V1StGXR8_Z5j",
  "status": "completed",
  "result": {
    "emails": [
      {
        "email": "team@zendaya.com",
        "confidence": 0.9
      }
    ],
    "profile": {
      "firstName": "Zendaya",
      "gender": "female",
      "country": "US",
      "state": "California",
      "city": "Los Angeles"
    },
    "socialProfiles": [
      { "platform": "tiktok", "url": "https://tiktok.com/@zendaya" }
    ]
  },
  "credits": { "charged": 1, "remaining": 259 }
}
200 ok · no email
{
  "handle": "somecreator",
  "platform": "tiktok",
  "customerId": "acct_V1StGXR8_Z5j",
  "status": "partial",
  "result": {
    "emails": [],
    "profile": {},
    "socialProfiles": []
  },
  "credits": { "charged": 0, "remaining": 259 }
}
200 ok · same creator again (cached, free)
{
  "handle": "zendaya",
  "platform": "instagram",
  "customerId": "acct_V1StGXR8_Z5j",
  "status": "completed",
  "cached": true,
  "result": {
    "emails": [
      {
        "email": "team@zendaya.com",
        "confidence": 0.9
      }
    ],
    "profile": {
      "firstName": "Zendaya",
      "gender": "female",
      "country": "US",
      "state": "California",
      "city": "Los Angeles"
    },
    "socialProfiles": [
      { "platform": "tiktok", "url": "https://tiktok.com/@zendaya" }
    ]
  },
  "credits": { "charged": 0, "remaining": 259 }
}
402 payment required · out of credits
{
  "error": "insufficient_credits",
  "remaining": 0,
  "buyUrl": "https://buy.stripe.com/…?client_reference_id=acct_V1StGXR8_Z5j"
}

Check credits

Your balance, the credit pack, your recent credit movements and what you spent on each item over the last 30 days. Free to call. You can also sign in, top right, with your Social Gems login to see your organization's balance, and as an admin, copy its API key.

GET/api/v1/account
curl
curl https://enrichment.socialgems.io/api/v1/account \
  -H "Authorization: Bearer $API_KEY"
200 ok
{
  "id": "acct_V1StGXR8_Z5j",
  "name": "Acme",
  "kind": "direct",
  "credits": 259,
  "pack": {
    "credits": 250,
    "priceUsd": 29,
    "buyUrl": "https://buy.stripe.com/…?client_reference_id=acct_V1StGXR8_Z5j"
  },
  "history": [
    { "at": "2026-10-21T10:12:05.904Z", "type": "usage", "credits": -1, "ref": "instagram:zendaya", "item": "contact" },
    { "at": "2026-10-20T16:02:11.517Z", "type": "credit", "credits": 250, "ref": "pi_3Q…", "note": "250-credit pack" },
    { "at": "2026-10-20T15:58:30.004Z", "type": "credit", "credits": 10, "ref": "free:acct_V1StGXR8_Z5j", "note": "free credits" }
  ],
  "usage": {
    "since": "2026-09-21T10:12:05.904Z",
    "items": [{ "item": "contact", "charges": 1, "credits": 1 }]
  }
}

Result object

Every 200 response includes a result object with the fields below. Missing optional fields are omitted.

FieldTypeDescription
emails[]arrayPrimary first. Each entry has email and confidence (0–1). Higher confidence means stronger evidence the address is deliverable.
profile.firstNamestringGiven name.
profile.countrystringISO 3166 alpha-2 code when recognized, otherwise the country name as reported.
profile.statestringState or region, where available.
profile.citystringCity, where available.
profile.genderenummale or female.
profile.phoneNumberstringContact phone when publicly listed.
socialProfiles[]arrayThe creator's accounts on other platforms (YouTube, TikTok, X, …), returned with the same lookup.

Rate limits

Limits are applied per API key. Hitting a limit returns 429 with a retryAt ISO timestamp.

TierLimitNotes
starter60 / minuteProduction workloads.
pro300 / minuteHigh-volume ingestion.
admin1,000 / minuteInternal only. Enables forceRefresh.

Errors

Errors return a JSON body with an error code. Some errors include additional fields (e.g. retryAt on 429).

StatusCodeWhen it happens
400invalid request bodyBody failed schema validation.
400invalid_handleHandle contains characters outside a–z, 0–9, . and _ (YouTube also allows -), has the wrong length, or is a YouTube channel id.
401missing bearer tokenAuthorization header is missing or malformed.
401invalid api keyKey not found or revoked.
402insufficient_creditsNo credits left for a creator you haven't unlocked. Buy a pack at buyUrl.
403account_not_foundThe key isn't linked to an account.
429rate_limitedTier limit exceeded. Retry after retryAt.
503upstream_unavailableThe creator database timed out or is rate-limited. Retry after Retry-After when present.
429 too many requests
{
  "error": "rate_limited",
  "retryAt": "2026-10-21T10:13:04.112Z"
}