GLM-5.3-Flash from scratch: what it does and how to use it with Node.js

GLM-5.3-Flash is a model from Zhipu’s GLM family. The official Z.ai documentation presents Flash and FlashX as multimodal models for working with images, code, and documents. For someone looking to get started, that opens up several possibilities: organizing data from an image, studying a problem, or preparing code for a small tool.

I tested it via Crazyrouter on October 30, 2026 with three tasks: an enrollment table, a probability exercise, and a page for splitting expenses. The table and exercise matched references calculated separately. The page needed a second request because the first left the code incomplete. I’ll work from those results and leave an example that can run without installing an SDK.

Before Connecting: Model, API, and Tools Are Separate Pieces

The identifier I used was glm-5.3-flash. FlashX is not part of these tests. GLM-5.3 has its own documentation, with text input and output; it’s not advisable to automatically attribute Flash’s characteristics to it or transfer results between similar names.

Flash’s output is text. It can be an explanation, a table, or a program. To save files, run that program, or act on an interface, you need an environment that carries out those actions.

Crop of an actual BigModel screenshot, with the original Chinese text. It describes document tasks that involve the use of tools. This is provider information, not a demonstration that all those formats were tested here.

In my requests I sent text and a PNG image. I didn’t test video or other document formats, and the model didn’t receive external tools. I ran the code from its responses afterward, as a separate verification.

First Call: Two Files and One Command

The API allows a program to send a task to the service and receive the response. The example uses a local script; you don’t need to download the model weights or develop a website to follow it.

You can also access it through BigModel or Z.ai’s official APIs. I’m using Crazyrouter here because it was the service used for testing. The key, address, and configuration must correspond to the same provider.

If you later want to explore other options for generating code, you have the Crazyrouter coding models directory. It’s a general list, not an exclusive Flash sheet. The script selects the model by its exact ID: glm-5.3-flash.

Prepare Node.js and the Key

Install Node.js 22 or a later version and verify the installation with node --version. Create a key in Crazyrouter’s API token management section. If you restrict the models available for that key, include glm-5.3-flash.

Create a folder and save only the key in api-key.txt. Don’t upload that file to a public repository or include it in a screenshot. In the same folder, save the following code as glm-flash-en.cjs.

This example poses the exercise in English. The original tests used Simplified Chinese; I didn’t make an additional call with this English version. The request construction and response handling were verified locally. The results in the article belong to the original calls and do not evaluate the quality of the English.

'use strict';
const fs = require('node:fs');
const path = require('node:path');
​
async function main() {
  const key = fs.readFileSync(path.join(__dirname, 'api-key.txt'), 'utf8').trim();
  if (!key || /\s/.test(key)) throw new Error('Invalid key file');
  const request = {
    model: 'glm-5.3-flash',
    messages: [
      {
        role: 'system',
        content: "Answer based on the problem's conditions. Distinguish calculations, assumptions, and unknown information. Do not claim to have executed code you haven't run."
      },
      {
        role: 'user',
        content: "Box A contains 4 red balls and 2 blue; box B contains 2 red and 4 blue. Box A is chosen with probability 1/3 and B with probability 2/3. Then 3 balls are drawn, in order and without replacement, from the same chosen box. It is known that at least one of the first two balls was red. Calculate the conditional probability that the third is red and the posterior probability of having chosen box A. Give exact fractions, step-by-step reasoning, and common errors. Include a Node.js program that verifies the answers by enumerating exactly the ordered draws and distinguishing each physical ball. Do not substitute enumeration with random simulation or claim to have run the code."
      }
    ],
    max_tokens: 24000,
    stream: false
  };
  const response = await fetch('https://api.crazyrouter.com/v1/chat/completions', {
    method: 'POST',
    redirect: 'error',
    signal: AbortSignal.timeout(720000),
    headers: { Authorization: 'Bearer ' + key, 'Content-Type': 'application/json' },
    body: JSON.stringify(request)
  });
  if (!response.ok) {
    console.error('Request failed. HTTP status: ' + response.status);
    process.exitCode = 1;
    return;
  }
  const data = await response.json();
  const choice = data.choices?.[0];
  const redact = value => String(value).split(key).join('[redacted]');
  console.log('Finish reason: ' + redact(choice?.finish_reason || 'not indicated'));
  const answer = choice?.message?.content;
  if (typeof answer !== 'string' || !answer.trim()) {
    console.error('Response is empty. Check output limit and supported parameters.');
    process.exitCode = 1;
    return;
  }
  console.log(redact(answer));
}
main().catch(() => {
  console.error('Could not complete the request. Check the local file, connection, and configuration. Sensitive error details are not shown.');
  process.exitCode = 1;
});

Run the Script and See What Actually Finished

Open a terminal in that folder and run:

node glm-flash-en.cjs

On Windows you can type powershell in the address bar of the folder open in File Explorer and press Enter. Make sure the file wasn’t saved as glm-flash-en.cjs.txt. You don’t need additional packages.

The command sends a real API request. Since it uses stream: false, it waits for the complete response before showing it. It prints the finish reason and content; it doesn’t automatically execute code the model may return.

An HTTP 401 indicates you need to check the validity of the key and the service that issued it. If file reading fails, check the name and location. If there’s no content or finish_reason: length appears, review the response and output limit before retrying. HTTP 200 confirms success at that level, but doesn’t demonstrate the result is complete or correct.

In an OpenAI-compatible client, the Base URL is https://api.crazyrouter.com/v1. The script uses the full endpoint, which adds /chat/completions. The API documentation index allows you to find other formats. This example sends text; for the visual test I used an image_url block.

Why I Chose a Probability Exercise for the Example

Box A has four red balls and two blue; B has two red and four blue. Box A is chosen with probability 1/3 and B with 2/3. Three balls are drawn from the same box, in order and without replacement. It is known that at least one of the first two was red. You need to calculate the probability that the third is red and the posterior probability of having chosen A.

In the original request, GLM-5.3-Flash obtained 13/32 and 7/16, respectively. The reasoning also matched the independent solution. These are useful values for comparison when you run the example, though the English response should be reviewed on its own.

Before sending the problem I prepared an exact enumeration of the draws. If each ball is distinguished from the others, there are 6 × 5 × 4 = 120 sequences per box. I also ran the JavaScript program the model returned: it calculated exact fractions using BigInt and produced the same values.

This case is useful for learning because new information changes the probability of having chosen each box. Also, drawing a ball modifies the composition for the next turn. An explanation that treats the three draws as independent is no longer solving the problem posed. Asking for the steps and comparing them is more useful than just keeping the final fraction.

It’s a test with a concrete problem, not a general measure of mathematical reliability.

Moving From an Image to Data You Can Use

The visual task started with a fictional twelve-row table. There were repeated identifiers with updates, cancellations, partial payments, and refunds. I asked to keep the most recent record of each ID, exclude cancellations from the active set, and separate payments and returns. I didn’t include the reference solution in the prompt.

The original image sent to the model, in Simplified Chinese. The columns are ID, category, total enrollment amount, amount paid, refund, status, and update time. The data are made up for testing. Amounts are kept in yuan, without conversion to euros or another currency.

I sent the PNG as a Base64 Data URL within image_url. The response kept nine updated records. The compared fields matched the image and the ten aggregate indicators with independent calculation. There were seven active enrollments: total amount to collect 720, net collection after refunds 580, and pending difference 140. The canceled ones totaled, separately, 200 paid and 80 refunded.

The model highlighted A004, canceled without a recorded refund, and A006, with partial payment. It didn’t invent a refund policy. Without knowing that policy or the reason for cancellation, an observation about the data doesn’t allow deciding by itself what refund corresponds.

For a class or activity list, I’d ask to keep the IDs in the response and show which rows went into each total. That way you can trace the result to its source. If it’s later incorporated into a program, calculation over the reviewed data can be done with conventional functions. That’s a usage proposal, not a second integration I tested here.

The scope is a single clear image. I didn’t measure behavior with blurry photos, merged cells, or multi-page documents.

The HTML Appeared Complete on the Second Try

The programming task asked for a standalone, usable HTML file without internet connection for splitting expenses. It should allow adding, renaming, and removing participants, choosing who each expense affected, editing it, deleting it, and resetting the data.

The splitting rule used integers in the smallest monetary unit. The remainder was to be distributed according to the order of people on screen. For example, 100.00 among three is divided into 33.34; 33.33 and 33.33. With 0.01, the split is 0.01; 0 and 0. Rounding each part separately should not change the total.

The first attempt ended with HTTP 200 and finish_reason: length, leaving code incomplete. The limit was 32,000 tokens; within that usage 25,707 reasoning tokens were recorded. So 32,000 was not available exclusively for visible HTML.

I kept the prompt and system message, and changed only max_tokens to 48,000. The second response ended with stop and contained complete HTML. I didn’t complete or manually fix it before testing it.

The complete script passed syntax checking. I ran its calculation and validation functions without changes in Node.js: they passed 40 checks, one of them with 132 combinations of amounts and participants. These included preserving the total, distributing the remainder in order, respecting selected participants, rejecting invalid amounts, and preventing removal of the only participant from an expense.

I didn’t check the interface in a real browser. There’s no test result for the visual design, buttons, editing, deleting, and resetting from the screen. What I got was complete code with the calculation part verified; to consider the tool ready, you’d need to go through those actions.

This second attempt also isn’t enough to claim that raising the limit always fixes a cut: a new response was generated and not a sufficient series to isolate that effect.

A Concrete Way to Get Started

The original series was three independent tasks and four requests, on October 30, 2026. All used glm-5.3-flash at https://api.crazyrouter.com/v1/chat/completions, without streaming. The limits were 16,000 tokens for the image, 24,000 for probability, and 32,000 and 48,000 for HTML. I didn’t explicitly set temperature, top_p, or reasoning_effort.

I’d start with something whose result you can judge: a small table with clear rules, an exercise with a verifiable solution, or an isolated function. If the goal eventually becomes an application, add the complete usage journey afterward. In these tests, reading data, explaining a calculation, and producing code required different verification forms; knowing that difference helps you choose the next task without attributing to the model results you haven’t seen yet.