kicksdk
Kick (kick.com) without the I/O, from one Rust core, for Rust, JavaScript, Python, Dart/Flutter and Elixir.
Docs: https://anisabdellatif.github.io/kicksdk/: install, every function in every language, each language's API reference, and the Kick reference.
kicksdk knows Kick: which requests to make and what the answers look like,
how the realtime chat feed speaks, how webhooks are signed. It never sends a
request or opens a socket itself. Your code does, with whatever it already
uses (fetch and WebSocket, Req and Mint, reqwest and tungstenite), and
hands kicksdk what came back. That is what lets one implementation run in a
browser, an extension, a Node service and a BEAM application alike, and when
Kick changes something, it is fixed once.
your HTTP / WebSocket client
│ ▲
request │ │ status + body, frames
▼ │
┌─────────────────────────────────────────────────┐
│ kicksdk (Rust) │
│ public_api · website · realtime · events · │
│ content · webhooks │
└─────────────────────────────────────────────────┘
crates.io npm (Wasm) PyPI (PyO3) pub (FFI) hex (NIF)
| Language | Package | How | Binaries |
|---|---|---|---|
| Rust | kicksdk on crates.io |
the crate | - |
| JavaScript / TypeScript | kicksdk-js on npm |
WebAssembly, plus connectChat |
one .wasm, everywhere |
| Python | kicksdk-py on PyPI (import kicksdk) |
PyO3, plus kicksdk.aio.connect_chat |
abi3 wheels for Linux (glibc, musl), macOS, Windows; sdist builds from source |
| Dart / Flutter | kicksdk on pub.dev |
dart:ffi over a C ABI, plus connectChat |
Linux, macOS, Windows, Android, iOS, downloaded by the build hook; builds from source otherwise |
| Elixir | kicksdk on hex |
a NIF (rustler_precompiled) | Linux (glibc, musl), macOS, Windows; builds from source otherwise |
| C | kicksdk-ffi on crates.io |
include/kicksdk.h |
the same libraries as Dart |
Precompiled binaries come from the GitHub release of the package's version and
are checked against checksums published with the package; a binary that does
not match is refused. Where none fits, every package builds from the Rust
sources it ships with. The Linux (glibc) binaries need, at least: glibc 2.17
for the x86_64, aarch64 and arm NIFs and the x86_64 Dart/C library, 2.18 for
the aarch64 Dart/C library, and 2.27 for the riscv64 NIF (the port's first);
on musl (Alpine), the Elixir NIF needs libgcc at run time: apk add libgcc.
docs/releasing.md has the details.
What it covers
| Module | Kick | |
|---|---|---|
public_api |
api.kick.com/public/v1, official |
App token, /livestreams and /channels (batched by 50), webhook subscriptions, the signing key |
website |
kick.com/api/v2, Kick's own frontend |
Chatroom id, follower total, live state, chat history and pin, user cards; moderating and sending as a viewer |
realtime |
Kick's chat feed (Centrifugo) | Where to connect, the guest token, and a Session state machine: connect, subscribe, pings, token refresh, reconnect timing |
events |
The feed's events | Chat messages, deletions, bans, pins, subs, gifted subs, Kicks, rewards, hosts, live/offline, polls, typed. Unknown events come out by name, never as errors |
content |
Message text | [emote:id:name] tokens split out, runs combined |
webhooks |
Official webhooks | Headers, RSA signature verification, typed bodies |
Everything rests on docs/KICK.md: every fact about Kick,
with where it was seen. Read it before relying on anything unofficial.
Two things to know first
The chat feed only accepts kick.com. Kick's realtime server refuses a
WebSocket whose Origin is not https://kick.com, and the endpoints that hand
out its address and tokens answer CORS for kick.com alone. A server sets the
header itself (the JS driver does in Node; set it on your Elixir or Rust
client). A browser cannot: there, the feed works on kick.com (a page, or an
extension's content script), and anywhere else needs a server to relay it.
Much of Kick is unofficial. The website API and the realtime feed are what
kick.com's own frontend uses, not a published API. They can change without
notice, and using them is a grey area under Kick's terms. The official API
(public_api, webhooks) is the stable part. Treat a failure of the rest as
a gap, never a zero.
Rust
[]
= "0.2"
use ;
let e = default;
let req = channel_request;
// … send it with your client …
let channel = parse_channel?;
let mut session = new;
// socket opened (Origin: https://kick.com):
for action in session.open
// each text frame:
for action in session.receive
JavaScript
bindings/js is the npm package: the core built to WebAssembly, plus
connectChat, which does the networking around a Session.
import * as kick from 'kicksdk-js';
await kick.;
const chat = kick.;
// later: chat.close()
examples/ has a small chat app for each language.
Python
Dart / Flutter
import 'package:kicksdk/io.dart';
final chat = connectChat(
chatroomId: chatroomId,
channelId: channelId,
onEvent: (e) => print(e['event']),
);
Elixir
bindings/elixir is the hex package, a NIF over the same core. Values come
back the way JSON decodes: maps with string keys, never atoms made from Kick's
data.
session = Kicksdk.Realtime.Session.new(Kicksdk.Realtime.chat_channels(chatroom_id, channel_id))
# socket opened, with Origin: https://kick.com
for %{"action" => "send", "text" => t} <- Kicksdk.Realtime.Session.open(session, token, now), do: send_frame(t)
# each frame
for action <- Kicksdk.Realtime.Session.receive(session, frame, now), do: handle(action)
Layout
crates/kicksdk/ the core (with `dispatch`: every function by name, JSON in and out)
crates/kicksdk-wasm/ its WebAssembly binding (built into bindings/js/pkg)
crates/kicksdk-ffi/ a C ABI over dispatch: the Dart package's library, and C's
crates/kicksdk-nif/ the Elixir NIF
bindings/js/ the npm package: the binding, connectChat, types
bindings/python/ the PyPI package: PyO3 over dispatch, kicksdk.aio
bindings/dart/ the pub package: dart:ffi, the build hook, connectChat
bindings/elixir/ the hex package
fixtures/ Kick's answers and frames, recorded and anonymized
docs/KICK.md everything known about Kick, with sources
docs/architecture.md how it is built, and why
scripts/ build-js, vendor-native (packages' source fallback), release-check
tools/ recorders
examples/ a minimal chat app per language
Building and testing
( && )
( && && )
( && ) # the build hook compiles the library with cargo
( && ) # compiles the NIF from source in a checkout
Every test suite reads the same fixtures/, so a recording of a Kick
change fails every binding until the core handles it. How to record one is in
CONTRIBUTING.md. Releasing is a tag: .github/workflows/release.yml
builds every binary, uploads it to the GitHub release, and publishes each
package.
License
Licensed under either of Apache License 2.0 or MIT, at your option. Not affiliated with Kick.