Your data, in your own tools.
Kults API v1 is read-only REST and JSON. It is off until the workspace owner turns it on in Settings > API, and it reads only through keys the owner makes.
Keys
In Settings > API the owner switches the API on and creates keys: each has a name, scopes, and either every creator or a chosen few. A key looks like kults_ plus 43 letters and digits, and is shown once. Kults keeps only a one-way hash of it. Turning the API off stops every key at once; revoking a key stops that one.
Send it on every request:
curl https://kults.io/api/v1/me \ -H "Authorization: Bearer kults_…"
Scopes: creators:read, fans:read, notes:read, money:read, messages:read, team:read, content:read.
Paging
Lists answer { data, next_cursor }, oldest change first. Pass next_cursor back as cursor until it is null. They also take creator (an id), updated_since (an ISO time, to pull only what changed) and limit (1 to 500, default 100). Money is in integer cents, times are ISO 8601 in UTC, ids are the ones the app uses.
curl "https://kults.io/api/v1/sales?updated_since=2026-11-01T00:00:00Z&limit=500" \ -H "Authorization: Bearer kults_…"
Errors and limits
401: no key, or an unknown, revoked or expired one.403: the API is off, or the key lacks the scope (the message says which).404: no such resource, or a fan or creator this key may not read.429: over 120 requests a minute for this key; wait forRetry-Afterseconds.400: a bad id, time, limit or cursor.
Errors answer { error: { code, message } }. Nothing is cached, and the API is not callable from a browser page (no CORS): call it from your server.
Resources
Examples use demo data.
GET /me
The workspace and the key you are using. Scope: any key.
{
"data": {
"workspace": {
"id": "b0000000-…",
"name": "Harbor Lane",
"timezone": "America/New_York"
},
"key": {
"name": "Reporting",
"scopes": [
"fans:read",
"money:read"
],
"creators": null
}
}
}GET /creators
Each creator: platform, handle, name, link status. Scope: creators:read.
{
"data": [
{
"id": "d0000000-0000-4000-8000-0000000000a1",
"platform": "passes",
"handle": "jadecoast",
"display_name": "Jade Coast",
"status": "connected",
"linked_at": "2026-10-02T14:03:11Z",
"updated_at": "2026-11-02T09:12:40Z"
}
],
"next_cursor": null
}GET /creators/{id}/kpis?from=2026-10-01&to=2026-10-31
The platform's own daily figures for one creator (gross and net by type, the parts inside them, subscribers per day), up to 400 days. Scope: money:read.
{
"data": {
"revenue": [
{
"day": "2026-10-01",
"type": "subscription",
"gross_cents": 48400,
"net_cents": 38720
}
],
"parts": [
{
"day": "2026-10-01",
"part": "message_tips",
"gross_cents": 2500,
"net_cents": 2000
}
],
"subscribers": [
{
"day": "2026-10-01",
"active": 412,
"new": 9,
"expired": 3,
"estimated": false
}
]
}
}GET /fans
Fans with spend, subscription, memberships, flags, last seen and follow date; their tags and team nickname only with notes:read. /fans/{id} gives one. Scope: fans:read.
{
"data": [
{
"id": "e0000000-0000-4000-8000-0000000000f1",
"creator_id": "d0000000-0000-4000-8000-0000000000a1",
"username": "harbor.fan",
"lifetime_spend_cents": 31200,
"subscription": {
"status": "active",
"renews_at": "2026-11-20T00:00:00Z",
"price_cents": 999
},
"memberships": [
{
"platform_plan_id": "pl-vip",
"status": "active",
"trial": false
}
],
"flags": {
"refunded": false,
"charged_back": false
},
"tags": [
"VIP"
],
"last_seen_at": "2026-11-02T22:10:00Z",
"followed_at": "2026-06-14T08:00:00Z",
"updated_at": "2026-11-02T22:11:05Z"
}
],
"next_cursor": "MjAyNi0xMS0wMlQyMjoxMTowNS…"
}GET /fans/{id}/notes
Your team's own notes on a fan. Scope: notes:read.
{
"data": [
{
"id": "f9a1…",
"fan_id": "e0000000-0000-4000-8000-0000000000f1",
"author_member_id": "c0000000-…",
"body": "Prefers morning messages",
"created_at": "2026-10-21T10:02:00Z"
}
],
"next_cursor": null
}GET /sales
Every sale: type, gross and net cents, when, the fan; the chatter it is credited to only with team:read. Scope: money:read.
{
"data": [
{
"id": "5c1e…",
"creator_id": "d0000000-0000-4000-8000-0000000000a1",
"fan_id": "e0000000-0000-4000-8000-0000000000f1",
"type": "tip",
"gross_cents": 2500,
"net_cents": 2000,
"currency": "USD",
"occurred_at": "2026-11-02T21:40:00Z",
"chatter_member_id": "c0000000-…",
"attribution": "chatter"
}
],
"next_cursor": null
}GET /reversals
Refunds and chargebacks, with the amount and the creator’s share. Scope: money:read.
{
"data": [
{
"id": "7b2d…",
"creator_id": "d0000000-0000-4000-8000-0000000000a1",
"fan_id": "e0000000-0000-4000-8000-0000000000f1",
"kind": "refund",
"gross_cents": 999,
"share_cents": 799,
"occurred_at": "2026-10-30T12:00:00Z"
}
],
"next_cursor": null
}GET /balances
Each creator's balance as last read. Scope: money:read.
{
"data": [
{
"creator_id": "d0000000-0000-4000-8000-0000000000a1",
"available_net_cents": 124500,
"total_net_cents": 980300,
"read_at": "2026-11-03T06:00:00Z"
}
],
"next_cursor": null
}GET /payouts
Payouts: amount, status and times, nothing about where they went. Scope: money:read.
{
"data": [
{
"creator_id": "d0000000-0000-4000-8000-0000000000a1",
"platform_payout_id": "po-118",
"status": "successful",
"amount_cents": 250000,
"created_at": "2026-10-28T09:00:00Z"
}
],
"next_cursor": null
}GET /messages
Message metadata: time, direction, reply time, price and whether it was bought; the chatter only with team:read. There are no message texts to give. Scope: messages:read.
{
"data": [
{
"id": "91aa…",
"creator_id": "d0000000-0000-4000-8000-0000000000a1",
"fan_id": "e0000000-0000-4000-8000-0000000000f1",
"direction": "out",
"chatter_member_id": "c0000000-…",
"sent_at": "2026-11-02T21:38:00Z",
"reply_seconds": 74,
"price_cents": 1500,
"unlocked_at": "2026-11-02T21:52:00Z"
}
],
"next_cursor": null
}GET /chatters, /shifts, /time-entries, /pay-statements
Your team, its shifts, clocked hours and pay statements. All but shifts need a key for every creator. Scope: team:read.
{
"data": [
{
"id": "c0000000-…",
"display_name": "Priya Shah",
"role": "chatter",
"active": true
}
],
"next_cursor": null
}GET /lists, /blasts, /media, /posts, /audience
Platform lists, paid messages, vault items and what they sold, posts, and each creator’s audience by day. Counts and money, never captions or files. Scope: content:read.
{
"data": [
{
"id": "3e9f…",
"creator_id": "d0000000-0000-4000-8000-0000000000a1",
"platform_post_id": "p-881",
"price_cents": 1200,
"unlock_count": 34,
"revenue_cents": 40800,
"likes": 212
}
],
"next_cursor": null
}GET /traffic
Passes tracking links, the referral account, discounts and trials with who is on each now, as last read (one answer, no paging). Scope: content:read.
{
"data": {
"links": [
{
"creator_id": "d0000000-0000-4000-8000-0000000000a1",
"name": "TikTok bio",
"clicks": 1840,
"signups": 61,
"spent_cents": 21250
}
],
"referrals": [],
"discounts": [
{
"creator_id": "d0000000-0000-4000-8000-0000000000a1",
"variant": "FREE_TRIAL",
"percent": 100,
"unique_fans": 12,
"fans": [
{
"fan_id": "e0000000-0000-4000-8000-0000000000f1",
"remaining_periods": 1
}
]
}
]
},
"next_cursor": null
}Never in the API
Platform sessions, cookies and tokens, message texts (they are never stored), vault files, sync internals, and anything of another workspace. Every read served is logged for the owner (time, key and resource) for 30 days, however it is called.