Docs · Integrators
For integrators
Everything here is derivable from the chain. The API is a convenience: read-only, cached, and never a bulk source of platform profile data.
Unauthenticated GET endpoints returning JSON. Responses are cached for a few seconds at the edge; please poll no faster than every 10 seconds. Amounts are strings of lamports (or base units of the quote mint).
| Endpoint | Query | Returns |
|---|---|---|
| GET /api/activity | limit, before, streamer=<platform>:<userId>, mint, kind, platform | Distributions and claims, newest first |
| GET /api/stats | none | Totals routed, claimed and unclaimed, 24h figures, slot |
| GET /api/streamers/search | q, platform=twitch|kick|pumpfun | Up to 10 matches: ids, login, channel link, vault PDA, status |
| GET /api/vaults/[pda] | none | Claimable, lifetime received and claimed, recent events |
| GET /api/leaderboard | tab, range=24h|7d|30d|all, platform | Ranked streamers, coins or claims |
| GET /api/payout/[platform]/[login] | none | Payout binding, evidence and sources for one streamer (public) |
curl "https://stacklive.xyz/api/activity?limit=5&kind=claim"While the site is in DEMO mode these endpoints return simulated data and include "demo": true where relevant.
Platform data rule
Twitch and Kick developer terms forbid re-syndicating their data, and pump.fun's terms reserve its data too. So the API returns platform, user ID, login and a channel link, and at most an avatar URL on the platform's own CDN. It never offers bulk profile exports, follower lists or cached images. If you need profile data, get it from the platform yourself.
The platform byte is part of every vault address and every attestation. Values 0 and anything above 3 are invalid (4 is reserved for a future platform).
| Byte | Platform | URL slug | user_id |
|---|---|---|---|
| 1 | Twitch | twitch | Numeric user ID, decimal ASCII (1 to 20 bytes)141981764 |
| 2 | Kick | kick | Numeric user ID, decimal ASCII (1 to 20 bytes)4598 |
| 3 | pump.fun live | pumpfun | Profile UUID as 32 lowercase hex, no dashes (32 bytes)d0d0a1c34f2b4e6d8a1f00000000c001 |
pump.fun live
pump.fun has no official public API. We read public profiles and the list of live coins through the endpoints pump.fun's own website uses, from our server, with short caches (30 seconds for live status, 5 minutes for profiles), and we always link back to pump.fun/profile/<username>. A pump.fun stream belongs to a coin, and only the coin's creator can go live on it; we attribute the stream to the creator's profile.
- Identity: the profile's
userId(a UUID), not the username and not the wallet. Usernames can be renamed; the UUID stays. Canonical form: 32 lowercase hex characters without dashes, so it fits the 32-byte seed limit exactly. - Profile wallet: the wallet that owns the profile (
canonical_svm_wallet), public on pump.fun. It signs the Sign-In With Solana message (method 5) and is also a public payout address (method 2). - No search: pump.fun has no working user search. Our search matches an exact username, profile link or wallet, plus the creators who are live right now.
A streamer's vault is a PDA of the vault program with seeds ["streamer", platform, user_id]. platform is one byte (1 = Twitch, 2 = Kick, 3 = pump.fun). user_id is ASCII: for Twitch and Kick the numeric user ID as canonical decimal (no leading zeros, 1 to 20 bytes); for pump.fun the profile UUID as 32 lowercase hex characters (32 bytes, the maximum length of a seed). Always use the ID, never the login: logins can change hands.
import { PublicKey } from "@solana/web3.js";
const PROGRAM_ID = new PublicKey("MockVau1t1111111111111111111111111111111111");
/** Twitch/Kick: decimal ID. pump.fun: profile UUID -> 32 lowercase hex, no dashes. */
export function canonicalUserId(platform: 1 | 2 | 3, userId: string): string {
if (platform === 3) {
const hex = userId.toLowerCase().replace(/-/g, "");
if (!/^[0-9a-f]{32}$/.test(hex)) throw new Error("pump.fun userId must be a UUID");
return hex;
}
if (!/^(0|[1-9][0-9]{0,19})$/.test(userId)) throw new Error("non-canonical user id");
return userId;
}
export function vaultPda(platform: 1 | 2 | 3, userId: string): [PublicKey, number] {
return PublicKey.findProgramAddressSync(
[Buffer.from("streamer"), Buffer.from([platform]), Buffer.from(canonicalUserId(platform, userId), "ascii")],
PROGRAM_ID,
);
}
// pump.fun: vaultPda(3, "d0d0a1c3-4f2b-4e6d-8a1f-00000000c001")use anchor_lang::prelude::*;
/// Twitch (1) / Kick (2): decimal ASCII. pump.fun (3): 32 lowercase hex of the profile UUID.
pub fn pump_user_id(uuid: &str) -> String {
uuid.chars().filter(|c| *c != '-').collect::<String>().to_ascii_lowercase()
}
pub fn vault_pda(platform: u8, user_id: &str, program_id: &Pubkey) -> (Pubkey, u8) {
assert!(!user_id.is_empty() && user_id.len() <= 32); // 32-byte seed limit
Pubkey::find_program_address(
&[b"streamer", &[platform], user_id.as_bytes()],
program_id,
)
}
// pump.fun: vault_pda(3, &pump_user_id("d0d0a1c3-4f2b-4e6d-8a1f-00000000c001"), &program_id)Check your implementation against these examples:
- Program ID DEMO
- MockVau1t1111111111111111111111111111111111
- Platform, user ID
- 1 (Twitch), "90000101"
- Vault PDA
- 8q3SLs9nc8DnFJbVVa2yAgxUjqX2EX9ztMdxDU3tny9t
- Bump
- 254
- Platform, user ID
- 3 (pump.fun), "d0d0a1c34f2b4e6d8a1f00000000c001"
- Vault PDA
- He8av2E7ZU6VErrRkt17vRx8Nuyhdwn9TuPGKaSbpQFQ
- Bump
- 255
The verify page runs the same derivation and the balance reads in your browser against a public RPC, with each step shown.
A browser-source page for OBS or Streamlabs that shows fees as they land. Build the URL on the overlay page, or write it by hand. Opted-out streamers render nothing.
https://stacklive.xyz/overlay/<platform>/<login>?preset=alert|ticker|corner&accent=mint&font=sans&bg=solid&scale=1&pos=brplatformistwitch,kickorpumpfun;loginis the channel name (for pump.fun, the profile username).preset:alertstays hidden until fees land,tickeris one line with today's total,corneris a small chip with today and unclaimed (addqr=1for a QR code).accentismint,lime,sky,amber,white,roseorhex:RRGGBB;fontissans,wide,condensedormono.bg=nonedraws on a transparent background;scaleruns from 0.5 to 3;minhides alerts below an amount in SOL.
pump.fun streamers: a browser source needs OBS (or another encoder). pump.fun supports that: on the coin page, Start livestream offers RTMP credentials (an RTMP URL and a stream key) to use in OBS, and the overlay works as a normal browser source there. If you stream straight from the webcam in the browser, there is no place to add an overlay.
To avoid depending on us at all, subscribe to the vault program's events (VaultInitialized, Claimed, RebindRequested, RebindFinalized, ClaimsPaused) and to pump_fees DistributeCreatorFeesEvent for sharing configs that list a vault PDA as a shareholder.
The split is per coin. Read each coin's shareholders and bps from its sharing config (or the UpdateFeeSharesEvent in the launch transaction) instead of assuming a fixed ratio: the protocol treasury always holds 1,000 bps, the streamer vault(s) hold 1,000 to 9,000 bps in total, and the launcher's wallet holds the remainder (absent when it is zero). A distribution's lamports go to each shareholder in those proportions.