Connect to any TikTok Live stream and receive real-time events — chat, gifts, likes, joins, viewer counts, and 64 decoded event types. No signing server, no API keys, no authentication required.
use piratetok_live_rs::TikTokLive;
use piratetok_live_rs::structs::TikTokLiveEvent;
#[tokio::main]
async fn main() {
// Connect to a live stream — handles auth, room resolution, and WSS automatically
let mut stream = TikTokLive::builder("username_here")
.connect()
.await
.unwrap();
// Each event is a fully decoded protobuf message; Err(ConnectionClosed) once the stream is done
while let Ok(event) = stream.next_event().await {
match event {
TikTokLiveEvent::Chat(msg) => {
let nick = msg.user.as_ref().map_or("?", |u| u.nickname.as_str());
println!("{nick}: {}", msg.comment);
}
TikTokLiveEvent::Gift(msg) => {
let nick = msg.user.as_ref().map_or("?", |u| u.nickname.as_str());
let gift = msg.gift_details.as_ref().map_or("gift", |g| g.gift_name.as_str());
let diamonds = msg.gift_details.as_ref().map_or(0, |g| g.diamond_count);
println!("{nick} sent {gift} ({diamonds} diamonds)");
}
TikTokLiveEvent::Like(msg) => {
println!("{} total likes", msg.total_like_count);
}
TikTokLiveEvent::Disconnected => break,
_ => {} // 64 decoded event types available
}
}
}[dependencies]
piratetok-live-rs = "0.3"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }| Language | Install | Repo |
|---|---|---|
| Go | go get github.com/PirateTok/live-go |
live-go |
| Python | pip install piratetok-live-py |
live-py |
| JavaScript | npm install piratetok-live-js |
live-js |
| C# | dotnet add package PirateTok.Live |
live-cs |
| Java | com.piratetok:live |
live-java |
| Lua | luarocks install piratetok-live-lua |
live-lua |
| Elixir | {:piratetok_live, "~> 0.1"} |
live-ex |
| Dart | dart pub add piratetok_live |
live-dart |
| C | #include "piratetok.h" |
live-c |
| PowerShell | Install-Module PirateTok.Live |
live-ps1 |
| Shell | bpkg install PirateTok/live-sh |
live-sh |
- Zero signing dependency — no API keys, no signing server, no external auth
- 64 decoded event types — chat, gifts, likes, joins, follows, shares, battles, polls, envelopes, and more
- Auto-reconnection — stale detection, exponential backoff, self-healing auth
- Enriched User data — badges, gifter level, moderator status, follow info, fan club, subscriber status
- Sub-routed convenience events —
Follow,Share,Join,LiveEndedfire alongside raw events
TikTokLive::builder("username_here")
.cdn(CdnEndpoint::Eu) // EU / US / Global (default)
.timeout(Duration::from_secs(15))
.heartbeat_interval(Duration::from_secs(10)) // default 10s
.max_retries(10) // default 5, 0 to disable
.stale_timeout(Duration::from_secs(90)) // default 60s
.proxy("socks5://user:pass@127.0.0.1:1080") // http(s)://, socks5://, socks5h:// (+ user:pass) for HTTP and WSS
.user_agent("Mozilla/5.0 ...") // override random UA pool with a fixed UA
.cookies("sessionid=abc; sid_tt=abc") // session cookies appended alongside ttwid
.language("de") // override auto-detected language (default from system locale)
.region("DE") // override auto-detected region (default from system locale)
.compress(false) // disable gzip for WSS payloads (default true)
.connect()
.awaituse piratetok_live_rs::http::api::{fetch_room_info, FetchParams};
// Normal rooms — no cookies needed
let info = fetch_room_info("ROOM_ID", FetchParams::default()).await?;
// 18+ rooms — pass session cookies from browser DevTools
let info = fetch_room_info("ROOM_ID", FetchParams {
cookies: Some("sessionid=abc; sid_tt=abc"), ..Default::default()
}).await?;Two sources, matching what the web/phone app shows:
// 1. Top-viewers box (the top-3 next to the counter) — free with WSS, no cookies
TikTokLiveEvent::RoomUserSeq(msg) => {
for c in msg.top_viewers() {
if let Some(u) = &c.user {
println!("#{} {} score={}", c.rank, u.nickname, c.score);
}
}
}// 2. Full roster (every named viewer in the room) — HTTP, login-gated
use piratetok_live_rs::http::api::{fetch_room_id, fetch_room_audience, AnchorId, FetchParams};
let room = fetch_room_id("username", FetchParams::default()).await?;
let audience = fetch_room_audience(&room.room_id, AnchorId::Known(&room.anchor_id), FetchParams {
cookies: Some("sessionid=abc; sid_tt=abc"), ..Default::default()
}).await?;
println!("{} in room ({} anonymous)", audience.total, audience.anonymous);
for v in &audience.viewers {
println!("#{} @{} ({}) score={}", v.rank, v.username, v.nickname, v.score);
}The roster endpoint needs session cookies (TikTok gates it behind login) but no
signing — no msToken, no X-Bogus. Without cookies it returns
TikTokLiveError::SessionRequired.
| Condition | Error |
|---|---|
statusCode 19881007 |
UserNotFound(username) |
| user exists, no active room / status ≠ 2 | HostNotOnline |
other non-zero statusCode |
ApiError(code) |
| HTTP 403/429, empty or non-JSON body | TikTokBlocked(reason) |
TikTokLiveEvent::Gift(gift) => {
if gift.is_combo_gift() {
if gift.is_streak_over() {
println!("x{} = {} diamonds", gift.repeat_count, gift.diamond_total());
}
} else {
println!("{} diamonds", gift.diamond_total());
}
}- Resolves username to room ID via TikTok JSON API
- Authenticates and opens a direct WSS connection
- Sends protobuf heartbeats every 10s to keep alive
- Decodes protobuf event stream into typed Rust structs
- Auto-reconnects on stale/dropped connections
All protobuf structs are hand-written with prost derive macros — no .proto files, no codegen, no build-time protoc dependency.
- TikTok only sets the
ttwidcookie on some anonymous requests, so a cookie-less response is retried up to 8 times (750 ms apart). A ttwid that still can't be fetched counts as a failed attempt and goes through the normal backoff. It never kills the stream. - The ttwid + user agent pair is reused across reconnects. It's rotated on
DEVICE_BLOCKED(2 s delay) and after a connection that dies within 30 s. - Backoff is 2 s → 4 s → 8 s → 16 s → 30 s cap.
max_retriescounts consecutive failures: a session that stayed up for 30 s resets the budget.
Full API reference (builder, config, stream, errors): docs/API.md.
cp .env.example .env # TIKTOK_USERNAME=..., optional TIKTOK_COOKIES=...
cargo run --features cli
cargo run --features cli -- --username tiktok --room-info
RUST_LOG=debug cargo run --features cli -- -u tiktok # verbose protocol loggingcargo run --example basic_chat -- <username> # connect + print chat events
cargo run --example online_check -- <username> # check if user is live
cargo run --example stream_info -- <username> # fetch room metadata + stream URLs
cargo run --example gift_tracker -- <username> # track gifts with diamond totals
cargo run --example gift_streak -- <username> # per-event gift deltas via GiftStreakTracker
cargo run --example profile_lookup -- <username> # look up profile data + HD avatars
cargo run --example audience -- <username> [cookies] # viewer names: top-3 box + full roster
cargo run --example like_debug -- <username> # debug like event patternsDeterministic cross-lib validation against binary WSS captures. Requires testdata from a separate repo:
git clone https://github.com/PirateTok/live-testdata testdata
cargo testTests skip gracefully if testdata is not found. You can also set PIRATETOK_TESTDATA to point to a custom location.
0BSD
