@jkt48connect-id/api
v1.0.1
Published
Official JKT48Connect SDK + CLI — one function call per feature. Live members, theater schedule, news, events, replays and realtime streams.
Maintainers
Readme
@jkt48connect/api
Official SDK for the JKT48Connect API. One function per feature — no fetch setup, no headers, no manual parsing.
npm i @jkt48connect/apiWorks in Node 18+, Next.js, TypeScript and plain JavaScript (ESM + CommonJS), with full type definitions. Ships with a CLI (jkt48 / jkt48connect) for testing every endpoint straight from your terminal.
CLI
npx @jkt48connect/api live # no install needed
jkt48 login jc_your_key # stored in ~/.jkt48connect/config.json (chmod 600)
jkt48 members
jkt48 member JKT48-Jesslyn
jkt48 theater --page 1
jkt48 recent-detail --limit 50 --type idn --sort gold --order desc
jkt48 shipping-cost --origin 1 --destination 2 --weight 1000
jkt48 watch live # realtime SSE, Ctrl+C to stop
jkt48 doctor # connectivity + latency per endpoint
jkt48 whoami # masked key only, never the full value
jkt48 --helpGlobal flags: --api-key, --json, --pretty, --quiet, --timeout <ms>, --retries <n>, --no-cache.
Output is JSON automatically when piped, so jkt48 live --json | jq '.[].name' just works.
Key resolution order: --api-key → JKT48_API_KEY → stored config.
Exit codes: 0 success, 1 error, 2 bad arguments, 3 unauthorized or quota exceeded.
Quick start
import { JKT48Connect } from "@jkt48connect/api";
const jkt48 = new JKT48Connect({ apiKey: process.env.JKT48_API_KEY! });
const live = await jkt48.getLive();
const members = await jkt48.getMembers();
const news = await jkt48.getNews();CommonJS:
const { JKT48Connect } = require("@jkt48connect/api");
const jkt48 = new JKT48Connect({ apiKey: process.env.JKT48_API_KEY });Keep your API key on the server. In Next.js call the SDK from a Server Component, Route Handler or Server Action — never from a
"use client"file.
Options
new JKT48Connect({
apiKey: "...", // required
timeout: 15000, // ms per attempt (default 15000)
retries: 2, // retries for transient failures (default 2)
cache: 30000, // in-memory GET cache in ms, or false (default 30000)
fetch: myFetch, // custom fetch implementation
allowBrowser: false // silence the browser warning if you proxy the key
});Every method also takes a final { signal, timeout, cache } argument:
const ctrl = new AbortController();
await jkt48.getLive({ signal: ctrl.signal, cache: false });Live
await jkt48.getLive(); // members live right now
await jkt48.getTopGifter({ roomId: 123456 }); // gift leaderboard
await jkt48.getRecent(); // sessions that just ended
await jkt48.getRecentDetail({ // full analytics + paging
limit: 50, offset: 0, type: "idn",
sort: "gold", order: "desc",
from: "2026-01-01", to: new Date(),
});Members
await jkt48.getMembers(); // full roster
await jkt48.getMember("JKT48-Jesslyn"); // one profile
await jkt48.getBirthdays(); // upcoming birthdaysTheater
await jkt48.getTheater({ page: 1 }); // schedule
await jkt48.getTheaterDetail("ABC123"); // setlist, lineup, ticket pricing
await jkt48.getIdnPlus(); // paid IDN+ showsNews, events and video
await jkt48.getNews({ page: 1 });
await jkt48.getNewsDetail("some-article-slug");
await jkt48.getEvents();
await jkt48.getReplay();
await jkt48.getYoutube();Shipping (merch)
const districts = await jkt48.getShippingDistricts("Bandung");
const rates = await jkt48.getShippingCost({
originDistrictId: districts[0].id,
destinationDistrictId: 1234,
weightGram: 1000,
});Realtime
Server-sent events with automatic reconnect. Both return a stop() function.
const stop = jkt48.onLive((event) => {
if (event.type === "live_start") console.log(`${event.member?.name} is live!`);
});
const stopPm = jkt48.onPm((msg) => console.log(msg.member, msg.message));
// later
stop();
stopPm();Errors
Every failure is a JKT48Error with a stable code, never a raw network error.
import { JKT48Error } from "@jkt48connect/api";
try {
await jkt48.getMember("nope");
} catch (err) {
if (err instanceof JKT48Error) {
console.error(err.code); // NOT_FOUND
console.error(err.status); // 404
console.error(err.retryable); // false
}
}| Code | Meaning |
| --- | --- |
| VALIDATION | Bad argument, caught before the request is sent |
| UNAUTHORIZED | Missing or invalid API key |
| FORBIDDEN | Key lacks access to this feature |
| NOT_FOUND | No such member, show or article |
| RATE_LIMITED | Too many requests — retried automatically |
| QUOTA_EXCEEDED | Plan quota reached |
| TIMEOUT | Attempt exceeded timeout |
| NETWORK | Connection failed |
| UPSTREAM | Service-side error — retried automatically |
| ABORTED | You aborted via signal |
| PARSE | Unexpected response body |
RATE_LIMITED, TIMEOUT, NETWORK and UPSTREAM are retried with exponential backoff and jitter (and honour Retry-After).
Security notes
- Connection details are resolved internally; the SDK exposes no endpoint or host constants and error messages, stacks and logs are sanitised so URLs, keys and IPs never leak.
- Inputs are validated before any request leaves your process.
- Zero runtime dependencies.
- An API key shipped to a browser is readable by anyone using that page — always call the SDK from your server.
License
MIT
