This is a plain overview of how the Xmax X2.0 API is structured, based on Xmax's official developer documentation at platform.xmax.ai. It covers authentication, the SDK, the six generation modes, and the data flow from camera to rendered output — useful whether you're planning an integration or just trying to understand what building on X2.0 involves. We're independent and not affiliated with Xmax; treat the official docs as the final word, and this page as a map of the terrain.
Xmax authentication uses a two-tier key system designed around a simple principle — never let a long-lived credential touch the browser:
uk- prefix) — issued to your account, meant to be held only on your backend server. This key should never be shipped to client-side code, embedded in a mobile bundle, or committed to a repo. In the SDK examples it's read from an environment variable, not hardcoded.tk- prefix) — short-lived tokens your backend generates on demand and passes to the frontend. Each temporary key carries a TTL (expiration time) and a defined usage allowance, so the frontend never holds a credential with unlimited scope or lifetime.In practice, this means every real integration needs a backend component whose job is to hold the permanent key and mint temporary keys for sessions — even if the rest of your app is a static frontend.
The backend requests a temporary key by calling POST https://api.xmaxai.com/open/api/v1/temporary-api-key. The permanent key travels in the X-Api-Key header (server-to-server only), and the JSON body carries two parameters that define the token's blast radius:
expireSeconds — how long the key stays valid, in seconds. The server converts this into an absolute expiry timestamp.pointsLimit — the total quota the key is allowed to spend before it stops working.The response returns the tk- token plus its expireTimestamp, pointsLimit, consumedPoints, and remainingPoints, so your frontend can display remaining budget and your backend can log usage. A temporary key cannot mint another temporary key — the API rejects that with a dedicated error code (46021) to prevent nested delegation and quota fan-out. Other errors worth handling include a missing header (46001) and an invalid permanent key (46002). This expireSeconds + pointsLimit pair is the heart of xmax authentication: it's how you translate a plan, a trial, or a single session into a credential the browser can safely hold.
The officially supported SDK is @xmaxai/sdk-global, a JavaScript package for browser environments (install it via npm, pnpm, or yarn). It's the SDK documented for building real-time X2.0 experiences today, and most of the xmax api documentation is written against it.
Xmax's documentation lists Python and native mobile SDKs (Kotlin, Swift) as in development. That means server-side generation pipelines and native iOS/Android apps don't currently have an official first-party SDK — teams needing those today would be building against the browser SDK's underlying API surface directly, or waiting for the additional SDKs to ship.
A live session with the xmax x2.0 api follows the same short sequence whether you're using a camera or a video file. Understanding these five calls is most of what you need to reason about an integration:
models.realtime("x2.0") builds the local configuration, and createXmaxClient({ apiKey }) creates a client — with the key coming from your temporary-key flow, never a hardcoded value.navigator.mediaDevices.getUserMedia({ video: { frameRate, width, height } }) requests the camera. The width, height, and frame rate here are capture constraints; the browser may adjust them to the camera's real capabilities.client.realtime.connect(stream, { model, remoteElement, size }) creates a session, joins the WebRTC channel, publishes your input stream, and binds the output <video> element. If you omit size, the SDK derives it from the input and aligns to multiples of 32.realtimeClient.set({ prompt, image }) begins transforming the stream. You can pass a prompt, a reference image URL, or both.realtimeClient.disconnect() closes the session, stops publishing, and leaves the WebRTC channel — release media resources here so the camera light turns off.Warmup happens during connect(); generation only starts at set(). That split lets you prepare the pipeline before committing to billed generation time.
X2.0 is one model with six mode presets, each pairing a tuned prompt with a reference-image pattern. On user-facing surfaces these carry product names; at the API level they're the technical identifiers below:
| Mode | What it does |
|---|---|
| CharX | Swaps the person's identity/face for a reference character (Face Swap) |
| ClothX | Swaps or applies an outfit onto the person in frame (Outfit Swap) |
| VibeX | Restyles the whole scene into a different visual style (Style Morph) |
| MoX | Animates a still frame, or lets you drag a trajectory to direct motion (Bring to Life) |
| DimX | Summons a virtual character into the live camera feed, AR-style (Summon) |
| Free | Fully custom prompt and reference image, for anything outside the presets (Freestyle) |
Selecting a mode in the API is effectively selecting which prompt and reference-image pattern gets applied to the incoming stream — the underlying model call is the same X2.0 model throughout. CharX, ClothX, VibeX, and DimX expect a reference image; MoX needs none and leans on trajectory control instead.
The API accepts two kinds of input through the same pipeline. Camera streaming uses getUserMedia() to publish a live feed. Video streaming feeds a pre-recorded clip through createVideoFileStream(file), which converts a file or URL into a publishable stream; you then build the model from that stream's dimensions and frame rate and pass inputKind: "file" to connect(). After disconnecting a file session, call mediaFileStream.destroy() to free its resources. The transform runs the same way either way — the only real difference is how you prepare the input.
MoX and interactive modes support directing motion by dragging on the output video. When you provide remoteElement, the SDK automatically builds an interactive drag surface, enabled by default once generation starts (pass drag: false to turn it off). For custom interactions, sendTracks() pushes control points manually — single-touch across frames or multi-touch in one frame. Coordinates are expressed against the content resolution you passed as size, not the on-screen display size: top-left is [0, 0] and bottom-right is [width - 1, height - 1]. This is what makes X2.0 output feel playable rather than merely watched.
The X2.0 pipeline has three stages:
<video> element in the page.Because transport is WebRTC and output is a plain <video> element, X2.0 is a real-time system end to end — you're not uploading a clip and polling for a finished render. For the concept behind that, see real-time video diffusion and real-time interactive video.
For reference images or video assets, the SDK handles uploads by sending files directly to Tencent COS (Cloud Object Storage). The helper client.files.uploadAndCheckImage(file) takes a file (typically from an <input type="file"> or drag-and-drop), validates that it's an image, uploads it, and returns an object whose url field is the public link you pass straight into set({ image }) as the reference image. The response also includes the COS object key, the temporary sts credentials used for the upload, and the raw COS response.
You don't manage storage credentials yourself — the SDK fetches short-lived STS credentials automatically. Type checking is strict: passing a video into the image helper throws immediately. Uploaded video URLs can be stored, replayed, or forwarded into your own downstream flows and advanced X2.0 patterns.
X2.0 is metered by generation time: 1 credit per second of output. New accounts receive a free credit allowance to test the API before paying. Beyond the free allowance, pricing is set on Xmax's official pricing page — check there directly for current rates (verify current pricing), since we won't reproduce specific numbers here that could go stale. Note the connection between billing and auth: the pointsLimit you set when minting a temporary key is what caps how many seconds of generation a given browser session can consume. For a plain-language walkthrough, see the xmax pricing overview.
Most real-world friction is operational rather than about the model itself:
uk- key ever reaches client code, rotate it. Only tk- belongs in the browser.disconnect() (and destroy() for file streams) leaves the camera active and resources held.Building directly on the xmax x2.0 api makes sense when you're shipping your own product with custom UX, your own account model, and your own billing on top of X2.0 — and when you can stand up the backend needed to hold the permanent key and mint temporary keys. If you just want to use the transformation, or to evaluate output quality before committing engineering time, a ready-made app is a faster path.
Everything above — key management, SDK installation, WebRTC session handling, mode selection, upload logic — is integration work. If your goal is to actually use X2.0's real-time transformation (not build a product on top of it), LiveGen (livegen.ai) is a consumer web app built on X2.0 that skips all of it. Open the browser, allow camera access, pick a mode, and the transformation runs — no uk-/tk- key pair, no SDK install, no backend required on your end. It's a useful way to see the API's actual output quality before deciding whether an integration is worth building. For more background, see what is Xmax X2.0 and how to use Xmax.
A permanent uk- key stays server-side; your backend issues short-lived tk- keys (with TTL and usage limits) to the frontend for each session.
@xmaxai/sdk-global, the official JavaScript browser SDK. It's currently the only officially supported SDK.
Not yet — Xmax lists a Python SDK as in development, without a public release at time of writing.
WebRTC, for low-latency real-time streaming between the input source and the rendered output.
The SDK uploads files directly to Tencent COS and returns a URL, which you use as the reference-image parameter in your API calls.
1 credit per second of generation, with a free credit allowance for new accounts. Check the official pricing page for current rates.
When your backend calls the temporary-key endpoint, pass expireSeconds for the TTL and pointsLimit for the total quota. The response returns the tk- token plus its expiry and remaining points, and a temporary key can never mint another one.
Yes. Use createVideoFileStream() to turn a file or URL into a publishable stream and pass inputKind: "file" to connect(); the rest of the pipeline is the same as camera input.
Track coordinates map to the content resolution you passed as size, not the displayed size of the video element — scale your points to [width - 1, height - 1].
To integrate the API, yes — you need a backend to hold the permanent key. To simply try the transformation, no: a consumer app like LiveGen runs it in the browser with no keys or backend on your end.
Open your camera and become anyone — free to start, no sign-up for your first try.
Start generating free