How biliTickerBuy Generates cToken and Browser Fingerprints for Bilibili Ticket API

biliTickerBuy generates cToken and browser fingerprints through two independent Python modules: a deterministic timing-based encoder in cptoken/__init__.py and a synthetic browser environment generator in util/request/BrowerState.py, both designed to emulate legitimate Bilibili client behavior.

The open-source tool biliTickerBuy (mikumifa/biliTickerBuy) implements sophisticated anti-detection mechanisms to interact with Bilibili's ticket purchase API. By synthesizing cryptographically plausible cTokens and realistic browser fingerprints, the tool mimics genuine user sessions. This article examines the exact implementation details, source file locations, and function signatures that power these generation routines.

Understanding cToken Generation in biliTickerBuy

The cToken is a base-64-encoded opaque token that encodes timing-related counters and browser state metrics. The generation logic resides entirely within cptoken/__init__.py and operates through a stateful runtime system.

The Core generate_ctoken Function

At lines 14-78 of cptoken/__init__.py, the generate_ctoken function constructs the binary payload. It accepts multiple integer fields—including m1, touchend, m2 through m9, timer, and timediff—and converts each to specific byte representations:

  • Single-byte fields use _b1 conversion
  • Larger counters use two-byte big-endian encoding
  • The final byte array is passed through base64.b64encode to produce the opaque string

This deterministic encoding ensures that identical input states produce identical cTokens, while the variability introduced elsewhere ensures uniqueness across requests.

Initializing Runtime State with CTokenRuntimeState

The CTokenRuntimeState class (lines 80-115) manages the raw counters and timestamps required for token generation. The init_ctoken_state function creates a fresh runtime using a derived deterministic function (derive_d) that mixes screen metrics, browser history length, user-agent length, and the current millisecond time (now_mod_256).

Key implementation details:

  • Fields m1 through m9 populate derived values based on browser window state
  • Fields like openWindow receive randomized small integers
  • The function logs the initial snapshot for debugging before returning the runtime object

The derivation logic appears at lines 92-113, where mathematical operations on window dimensions and temporal data create the initial entropy.

Simulating Request Snapshots

To mimic realistic user interaction patterns, the sim_ctoken_state function (lines 334-366) creates temporal snapshots that simulate the passage of time between requests. This function:

  • Accepts a previous state and current millisecond timestamp
  • Applies small random increments to touchend, openWindow, and visibilitychange counters
  • Returns a CTokenSnapshot via the snapshot method (lines 150-176)

The snapshot method updates the timer based on elapsed seconds and incorporates any timediff accumulated since the ticket collection timestamp. This allows the buying workflow to generate fresh "prepare" tokens for each API request while maintaining temporal consistency.

Typical implementation in the buy flow:

from cptoken import init_ctoken_state, sim_ctoken_state
import time

# Initialise a cToken runtime state (once per process)

runtime = init_ctoken_state(
    browser_window_state=generate_browser_window_state(),
    ticket_collection_t=int(time.time() * 1000)
)

# When preparing a request, obtain a fresh token snapshot

prepare_snapshot = sim_ctoken_state(runtime)
prepare_ctoken = prepare_snapshot.generate_prepare_ctoken()
payload["ctoken"] = prepare_ctoken

This pattern appears in task/buy.py at lines 470-482, where the generated token attaches to the prepare request payload.

Browser Fingerprint Generation Implementation

All fingerprint synthesis occurs in util/request/BrowerState.py (note the intentional misspelling in the filename). This module constructs a JSON-like structure mimicking real browser fields including window dimensions, navigator properties, WebGL contexts, and canvas hashes.

Browser State Data Structures

Lines 14-99 define TypedDicts that enforce the exact schema expected by Bilibili's API:

  • BrowserWindowState: Inner/outer dimensions, scroll offsets, screen positioning
  • BrowserDisplayState: devicePixelRatio, colorDepth, pixelDepth
  • BrowserNavigatorState: User-agent strings, platform, hardware concurrency
  • BrowserWebGLState and BrowserCanvasState: Graphics rendering fingerprints

These type definitions ensure that generated fingerprints match the expected server-side validation patterns.

Generating Individual Browser Components

The module provides discrete generators for each browser subsystem:

generate_browser_window_state (lines 167-268) selects realistic screen resolutions and calculates inner/outer window dimensions. It randomizes between maximized and windowed layouts while computing plausible scroll offsets.

generate_browser_display_state (lines 274-300) derives display metrics from the selected screen size, producing consistent devicePixelRatio values appropriate for the purported hardware.

generate_browser_navigator_state (lines 308-666) constructs Chrome-style user-agent strings, platform identifiers, language lists, and hardware concurrency counts that match the selected operating system profile.

Additional generators handle locale (generate_browser_locale_state), geolocation (generate_browser_location_state), WebGL vendor/renderer strings (generate_browser_webgl_state), and canvas hashing (generate_browser_canvas_state).

generate_browser_fingerprint_state (lines 622-705) orchestrates these helpers into a single BrowserFingerprintState dictionary. This function is deliberately pure-functional; the generated fingerprint should be cached and reused across multiple requests to maintain internal consistency (e.g., identical canvas hashes and WebGL fingerprints throughout a session).

Building Request Headers from Browser State

The build_headers_from_browser_state function (lines 801-880) transforms the fingerprint dictionary into legitimate HTTP headers. It produces:

  • User-Agent and Accept-Language headers
  • Sec-CH-UA client hints
  • Cookie strings and request metadata
  • Referrer policies matching the synthetic browser state

Integration example:

from util.request.BrowerState import generate_browser_fingerprint_state, build_headers_from_browser_state

# Generate a full fingerprint dict (once per session)

fingerprint = generate_browser_fingerprint_state(
    os_name="windows",
    locale="zh-CN"
)

# Build request headers from the fingerprint

headers = build_headers_from_browser_state(
    state=fingerprint,
    referer="https://show.bilibili.com/",
    base_headers={"X-Requested-With": "XMLHttpRequest"}
)

# Use these headers when issuing Bilibili API calls

response = requests.post(
    url="https://show.bilibili.com/api/ticket/prepare",
    json=payload,
    headers=headers
)

This pattern appears in util/request/BiliRequest.py (lines 13-31), where the generator initializes the state and constructs headers for HTTP requests.

Summary

  • cToken generation occurs in cptoken/__init__.py through deterministic encoding of timing counters and browser metrics, using CTokenRuntimeState for lifecycle management and sim_ctoken_state for temporal simulation.
  • Browser fingerprint synthesis happens in util/request/BrowerState.py, where typed dictionaries enforce API-compliant structures across window, display, navigator, WebGL, and canvas attributes.
  • Integration requires caching the browser fingerprint for session consistency while generating fresh cToken snapshots for each API request, as demonstrated in task/buy.py.
  • Both systems employ pure Python implementations with deterministic seeds to ensure reproducibility while introducing sufficient variability to avoid detection patterns.

Frequently Asked Questions

What is the purpose of the cToken in biliTickerBuy?

The cToken serves as an opaque authentication credential required by Bilibili's ticket API. According to the source code in cptoken/__init__.py, it encodes temporal state (timestamps, delays) and browser interaction metrics (window states, touch events) into a base-64 string that validates the client as a legitimate browser session rather than an automated script.

Why does the browser fingerprint generator use TypedDicts?

The TypedDicts defined in util/request/BrowerState.py (lines 14-99) enforce compile-time type safety for the complex nested structures expected by Bilibili's anti-bot systems. By strictly typing fields like devicePixelRatio, hardwareConcurrency, and WebGL vendor strings, the code ensures that generated fingerprints match the exact schema that server-side validation logic expects, reducing the risk of request rejection due to malformed JSON.

How does sim_ctoken_state prevent detection during rapid requests?

The sim_ctoken_state function in cptoken/__init__.py (lines 334-366) introduces micro-variations in timing fields between sequential requests. By incrementing touchend, openWindow, and visibilitychange counters with small random values and updating the timer based on actual elapsed milliseconds, it simulates the natural irregularity of human browsing patterns. This prevents the identical cToken repetition that automated detection systems often flag as bot behavior.

Can the browser fingerprint be reused across multiple ticket purchase attempts?

Yes, and the source code explicitly recommends this approach. The generate_browser_fingerprint_state function (lines 622-705) is designed to be pure-functional, meaning it should be called once per session and cached. Reusing the same fingerprint ensures consistency in critical identifying fields like canvas hashes, WebGL vendor/renderer strings, and user-agent across all requests in a purchase flow, whereas regenerating these values mid-session would trigger consistency checks on Bilibili's servers.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →