Guides / Build with / TypeScript SDK

TypeScript SDK

Zero-dependency TypeScript and JavaScript client built on Web-standard fetch, FormData and Blob: runs on Node 18+, Bun, Deno, Cloudflare Workers and Next.js Edge, with immutable matrix scoping, async pagination, multipart publishing and decrypt inspectors.

Installation & runtimes

Ships dual ESM + CommonJS bundles with strict .d.ts declarations and zero runtime dependencies. Runs unmodified on Node.js 18+, Bun, Deno, Cloudflare Workers, and Next.js Server Actions / Edge Runtime:

npmpnpmBunDeno
npm install tikultra

Quickstart & credit metadata

Instantiate TikUltra with an explicit apiKey or let it read process.env.TIKULTRA_KEY (or Deno.env). Every call resolves to a typed APIResponse<T> carrying res.data, res.creditsCost, res.creditsRemaining, res.creditsTier and res.requestId:

TypeScript SDKquickstart.ts
1import { TikUltra } from 'tikultra'
2
3const client = new TikUltra({ apiKey: process.env.TIKULTRA_KEY, os: 'ios', appFlavour: 'tiktok', region: 'US' })
4
5const [video, profile, search, pdp] = await Promise.all([
6  client.video.get({ aweme_id: '7707063215498399006' }),
7  client.user.profile({ username: 'tiktok' }),
8  client.search.query({ keyword: 'mechanical keyboards', count: 10 }),
9  client.shop.pdp({ product_id: '1729487391071113352' }),
10])
11
12console.log(video.data.aweme_id, profile.data.user?.unique_id, search.data.items?.length)
13console.log({ cost: pdp.creditsCost, remaining: pdp.creditsRemaining, requestId: pdp.requestId })

Namespaces map 1:1 to gateway domains: client.video, client.user, client.feed, client.search, client.tako, client.creator, client.douyin, client.shop, client.live, client.series, client.music, client.hashtag, client.account, client.devices, client.crypto and client.jobs, plus top-level client.resolve(...), client.idConverter(...), client.translate(...) and client.request(...).

Immutable matrix scoping & per-call overrides

client.withMatrix(...) clones a lightweight child client bound to any of the 8 app flavours and 95+ countries (84 cities) without mutating the parent instance, while { matrix } overrides individual calls:

TypeScript SDKmatrix.ts
1import { TikUltra } from 'tikultra'
2
3const client = new TikUltra({ apiKey: process.env.TIKULTRA_KEY, os: 'ios', appFlavour: 'tiktok', region: 'US' })
4
5// Immutable child client pinned to Android TikTok Shop in London
6const ukShop = client.withMatrix({ os: 'android', appFlavour: 'shop', region: 'GB', city: 'london' })
7const ukCatalog = await ukShop.shop.search({ keyword: 'ceramide moisturizer', count: 20 })
8
9// Per-call matrix override on a single request without creating a child client
10const jpFeed = await client.request('GET', '/v1/feed/persona-fyp', {
11  params: { niche: 'specialty coffee', count: 12 },
12  matrix: { os: 'ios', appFlavour: 'tiktok', region: 'JP', city: 'tokyo' },
13})
14console.log(ukCatalog.creditsCost, jpFeed.data.items?.length)

Async generator pagination

client.paginate(...) returns an AsyncGenerator<APIResponse> over any paginated GET or POST route, advancing cursor automatically until has_more is false or maxPages is reached:

TypeScript SDKpaginate.ts
1import { TikUltra } from 'tikultra'
2
3const client = new TikUltra({ apiKey: process.env.TIKULTRA_KEY })
4
5for await (const page of client.paginate('GET', '/v1/video/comments', { params: { aweme_id: '7705852133855161377', count: 50 }, maxPages: 10 })) {
6  for (const comment of page.data.comments ?? page.data.items ?? []) {
7    console.log(comment.cid, comment.text)
8  }
9  console.log({ cursor: page.cursor, hasMore: page.hasMore, creditsCost: page.creditsCost })
10}

Multipart video & slideshow publishing

client.account.uploadVideo and client.account.uploadSlideshow accept Web Blob, File, Uint8Array, ArrayBuffer, { filename, data, contentType } objects, or Node.js file paths:

TypeScript SDKpublish.ts
1import { TikUltra } from 'tikultra'
2
3const client = new TikUltra({ apiKey: process.env.TIKULTRA_KEY })
4
5const videoPost = await client.account.uploadVideo({
6  account_id: 'acc_creator_01',
7  file:       './launch.mp4', // or Blob / File / Uint8Array on Edge & Workers
8  caption:    'Shipping the TypeScript SDK today #typescript #devtools',
9  visibility: 'public',
10})
11
12const carousel = await client.account.uploadSlideshow({
13  account_id: 'acc_creator_01',
14  files:      [new Blob([slide1Bytes], { type: 'image/jpeg' }), './slide_2.jpg'],
15  title:      'Edge latency benchmarks',
16  caption:    'Zero dependencies across Node, Bun, Deno & Workers',
17})
18console.log(videoPost.data.aweme_id, carousel.data.aweme_id)

Decrypt inspectors & typed errors

client.crypto includes first-class helpers for all 8 protocol inspection primitives (mssdkDecrypt, xArgusDecrypt, xLadonDecrypt, xGorgonDecrypt, xCylonsDecrypt, ttencryptDecrypt, zstdDecompress, protobufDecode), and retries 429/5xx automatically before throwing typed error classes:

TypeScript SDKdecrypt_and_errors.ts
1import {
2  AuthenticationError,
3  InsufficientCreditsError,
4  RateLimitError,
5  SessionExpiredError,
6  TikUltra,
7  UpstreamError,
8} from 'tikultra'
9
10const client = new TikUltra({ apiKey: process.env.TIKULTRA_KEY, maxRetries: 3 })
11
12try {
13  const argus  = await client.crypto.xArgusDecrypt({ x_argus: process.env.CAP_X_ARGUS!, os: 'android' })
14  const ladon  = await client.crypto.xLadonDecrypt({ x_ladon: process.env.CAP_X_LADON! })
15  const gorgon = await client.crypto.xGorgonDecrypt({ x_gorgon: process.env.CAP_X_GORGON! })
16  const cylons = await client.crypto.xCylonsDecrypt({ x_cylons: process.env.CAP_X_CYLONS! })
17  const mssdk  = await client.crypto.mssdkDecrypt({ operation: 'report', payload_base64: process.env.CAP_MSSDK! })
18  const ttenc  = await client.crypto.ttencryptDecrypt({ ciphertext_base64: process.env.CAP_TTENC! })
19  const uzstd  = await client.crypto.zstdDecompress({ data_base64: process.env.CAP_ZSTD! })
20  const proto  = await client.crypto.protobufDecode({ hex: uzstd.data.hex })
21  console.log(argus.data, ladon.data, gorgon.data, cylons.data, mssdk.data, ttenc.data, proto.data.fields)
22} catch (err) {
23  if (err instanceof RateLimitError) console.error('Retry after (s):', err.retryAfter, err.requestId)
24  else if (err instanceof InsufficientCreditsError) console.error('Balance exhausted:', err.code)
25  else if (err instanceof SessionExpiredError) console.error('Re-import session:', err.requestId)
26  else if (err instanceof AuthenticationError) console.error('Invalid API key:', err.status)
27  else if (err instanceof UpstreamError) console.error('Upstream 5xx (0 credits):', err.requestId)
28}