Swello API.

Quickstart · v1

TikTok account and video data over a plain REST API. One key, four endpoints, and a response shape that doesn't move under you.

01

Your key

You were given a key that looks like sw_live_…. Send it as a bearer token on every request.

Terminal
curl https://api.swello.ai/v1/accounts/swello \
  -H "Authorization: Bearer sw_live_your_key_here"

Keep it server-side. It carries your whole allowance, and anyone holding it can spend it. If it leaks, ask us to reissue — we can't recover a key, only replace it.

02

The endpoints

Every successful response is a single data object. Fields are always present — a value we don't have is null, never missing.

GET /v1/accounts/{handle} Account by handle
Response
{
  "data": {
    "object": "account",
    "platform": "tiktok",
    "id": "6626035987700531000",
    "handle": "swello",
    "display_name": "Swello",
    "bio": "posting, handled.",
    "avatar_url": "https://…",
    "verified": false,
    "private": false,
    "region": "US",
    "stats": {
      "followers": 1234,
      "following": 5,
      "posts": 40,
      "likes": 900
    },
    "status": "active",
    "retrieved_at": "2026-09-02T19:00:00.000Z"
  }
}
GET /v1/videos/{id} Video by post id
Response
{
  "data": {
    "object": "video",
    "id": "6977747303692078337",
    "status": "live",
    "url": "https://www.tiktok.com/@bob/video/6977747303692078337",
    "caption": "day 14 of posting every day",
    "posted_at": "2021-06-25T15:03:19.000Z",
    "author": { "id": "9", "handle": "bob", "display_name": "Bob" },
    "sound": { "id": "77", "title": "original sound", "author": "bob" },
    "sound_start_ms": 4200,
    "stats": {
      "views": 18400,
      "likes": 2100,
      "comments": 96,
      "shares": 41,
      "saves": 310
    },
    "retrieved_at": "2026-09-02T19:00:00.000Z"
  }
}
POST /v1/sweep Roster in, verdicts out
Request · up to 200 items
{
  "accounts": ["swello", "matts.locked"],
  "videos": ["6977747303692078337"]
}
Response
{
  "data": {
    "object": "sweep",
    "checked": 3, "alive": 2, "gone": 1, "unknown": 0,
    "accounts": [
      { "handle": "swello", "status": "alive", "followers": 1234, "posts": 40 },
      { "handle": "matts.locked", "status": "gone", "followers": null, "posts": null }
    ],
    "videos": [
      { "id": "6977747303692078337", "status": "alive", "handle": "bob", "views": 18400 }
    ],
    "swept_at": "2026-09-02T19:00:00.000Z"
  }
}
What a sweep costs

One request per item checked — a 60-account roster spends 60. Items we couldn't reach come back "unknown" and are refunded, so you only pay for answers. The X-Quota-Cost header reports what was actually billed.

A roster that would overrun your allowance is refused whole rather than half-run, so you never get a partial answer you've already paid for.

GET /v1/usage Free — never consumes quota
Response
{
  "data": {
    "object": "usage",
    "plan": "growth",
    "period": "2026-09",
    "used": 18422,
    "included": 100000,
    "remaining": 81578,
    "rate_limit_per_minute": 120,
    "resets_at": "2026-10-01T00:00:00.000Z",
    "today": {
      "date": "2026-09-02",
      "used": 412,
      "limit": 8000,
      "remaining": 7588
    }
  }
}

Your limits

Your key carries up to three independent limits. /v1/usage reports where you stand on each — and it's free, so check it as often as you like.

LimitYou'll seeResets
Per minute429 rate_limitedWithin 60 seconds
Per day429 daily_cap_reachedMidnight UTC
Per month402 quota_exhaustedresets_at says when

The daily cap is there to protect you: a job stuck in a retry loop burns one day instead of your whole month.

Scoped keys

Some keys are restricted to a specific list of accounts. If yours is, a request for a handle outside that list returns 403 out_of_scope. For account lookups that check happens before anything else, so it costs you nothing; for videos it happens once we can see who the author is. Need an account added to your list? Ask us.

03

Two things that surprise people

status is the answer, not an error.

A handle or post that no longer exists comes back 200 with "status": "unavailable", not a 404. That's deliberate: if you're monitoring a roster, every row keeps the same shape and you can diff them directly. It also means a deleted post and a banned account look identical from here — we report that it's gone, and we don't guess why.

sound_start_ms belongs to the post, not the sound.

The start offset is a property of the video. A bare sound link always begins at 0:00, no matter where the original creator started it.

04

Errors

Every error has the same shape, so one handler covers all of them.

Error envelope
{ "error": { "code": "quota_exhausted", "message": "…" } }
StatusCodeWhat to do
400invalid_handleFix the parameter and retry.
400roster_too_largeA sweep takes 1–200 items. Split the roster.
401unauthorizedKey missing, malformed, or revoked.
402quota_exhaustedMonthly allowance spent. resets_at says when it returns.
403out_of_scopeThis key isn't permitted to read that account.
429rate_limitedToo fast. Honour Retry-After — 60 seconds.
429daily_cap_reachedToday's cap is spent. Resets at midnight UTC.
503temporarily_unavailableOur side. Retry after ~30s. You are not billed.

Useful response headers: X-Quota-Remaining, X-RateLimit-Limit, and X-Cache (HIT or MISS).

Billing rule

You're billed when we return data — cache hit or fresh fetch alike. You are not billed for 429s, 503s, or /v1/usage. You never pay for our outage.

05

Using it from Claude

Add the Swello MCP server and Claude can answer questions about accounts and posts directly — "how many followers does @x have?", "is this post still up?". Save mcp-server.js somewhere permanent, then add this block to your config.

Claude Code · ~/.claude.json
{
  "mcpServers": {
    "swello": {
      "command": "/usr/local/bin/node",
      "args": ["/absolute/path/to/mcp-server.js"],
      "env": { "SWELLO_API_KEY": "sw_live_your_key_here" }
    }
  }
}

Claude Desktop takes the same block, in ~/Library/Application Support/Claude/claude_desktop_config.json on macOS or %APPDATA%\Claude\claude_desktop_config.json on Windows. Restart Claude after editing.

The one thing that trips everyone

Use absolute paths for both command and args. A Claude launched from the dock doesn't inherit your shell's PATH, so a bare node fails with ENOENT and the server simply never appears — no error, just missing tools. Find your path with which node.

Confirm it worked by asking Claude "what Swello tools do you have?" — it should list four. Then try "sweep these accounts and tell me which are gone."

ToolTakesAnswers
swello_accounta handleFollower counts, profile details, whether the account still exists.
swello_videoa post idViews, likes, comments, caption, and whether the post is still up.
swello_sweephandles and/or idsChecks a whole roster at once — which accounts and posts are gone.
swello_usagenothingRequests used and remaining this period.

Requires Node 18 or newer. There's no npm install — the server has zero dependencies.

06

Getting the most from your allowance

  • Responses are cached briefly — accounts about 10 minutes, videos about 5. Polling the same handle in a tight loop spends your quota and hands back identical bytes; X-Cache: HIT tells you it happened. Poll on the order of minutes, not seconds.
  • Check /v1/usage on a schedule, not per request. It's free, and a daily or hourly check is enough to catch a runaway job before it drains a month.
  • Back off on 429. Honour Retry-After rather than retrying immediately — hammering a rate limit only extends it.
  • Batch overnight work. Your per-minute limit is separate from your monthly one, so a large roster sweep goes faster spread across minutes than fired all at once.