GLM-5.2 on OpenRouter: Configure Routing and Verify Tools
Independent research — not an official Z.ai publication.Identity and provider disclosure
Original editorial diagram of the tested routing choices. It is not an OpenRouter, Z.ai, Ionstream or Novita product screen. The later receipt image is rendered from the sanitized live responses.
OpenRouter gives one model slug to multiple inference backends. That
convenience changes the deployment contract. The string z-ai/glm-5.2 can
remain constant while the selected provider, precision, context limit,
parameter support, latency and token price change.
A working HTTP 200 therefore answers only the first question. Production code also needs to prove which provider ran the request, whether every required parameter survived routing, whether fallback is allowed, what the selected route cost, and how the application behaves when no endpoint qualifies.
This guide supplies that verification layer. It uses raw, dependency-free Python so framework behavior cannot hide the request. If you need a client adapter, use the companion LangChain GLM-5.2 setup after this route contract passes.
Navigate the OpenRouter route test
Section titled “Navigate the OpenRouter route test”- Separate the model from the route
- Copy the minimal tested request
- Inspect the live endpoint catalog
- Choose dynamic, ordered or pinned routing
- Read the Docker receipts
- Validate a tool call
- Reconstruct selected-route cost
- Apply privacy filters deliberately
- Troubleshoot by failure layer
- Promote a deployment contract
- Review practical questions
- Audit sources and method
Separate the model from the route
Section titled “Separate the model from the route”Use these three identifiers exactly:
| Layer | Value | What it selects |
|---|---|---|
| API base | https://openrouter.ai/api/v1 |
OpenRouter’s OpenAI-compatible gateway |
| Model ID | z-ai/glm-5.2 |
The GLM-5.2 model group inside that gateway |
| Credential | OPENROUTER_API_KEY |
Your OpenRouter account and spend boundary |
Do not send the OpenRouter slug to Z.ai’s first-party endpoint, and do not send a Z.ai Coding Plan key to OpenRouter. Coding Plan is a different subscription product with a supported-tool policy. The Coding Plan versus API guide keeps those commercial routes separate.
Z.ai’s GLM-5.2 guide documents the publisher’s text modality, one-million-token context, 128K maximum output, reasoning, function calling, caching and structured output. OpenRouter’s model view groups third-party endpoints under that model. A grouped model page does not make every endpoint identical to the first-party API.
Copy the minimal tested request
Section titled “Copy the minimal tested request”Keep the key outside code:
export OPENROUTER_API_KEY="your-openrouter-api-key"test -n "$OPENROUTER_API_KEY" && echo "OPENROUTER_API_KEY is set"This standard-library script reproduces the successful dynamic text profile:
import jsonimport osimport urllib.request
url = "https://openrouter.ai/api/v1/chat/completions"payload = { "model": "z-ai/glm-5.2", "messages": [ { "role": "user", "content": ( "Return exactly OPENROUTER_DEFAULT_OK " "and no other text." ), } ], "temperature": 0, "max_tokens": 128, "reasoning": {"effort": "none", "exclude": True}, "provider": { "allow_fallbacks": True, "require_parameters": True, "data_collection": "deny", "sort": "latency", },}
request = urllib.request.Request( url, data=json.dumps(payload).encode(), method="POST", headers={ "Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}", "Content-Type": "application/json", "HTTP-Referer": "https://example.com/", "X-OpenRouter-Title": "GLM-5.2 route check", },)
with urllib.request.urlopen(request, timeout=60) as response: body = json.load(response)
choice = body["choices"][0]content = (choice["message"].get("content") or "").strip()assert content == "OPENROUTER_DEFAULT_OK"assert choice["finish_reason"] == "stop"assert body["model"] == "z-ai/glm-5.2"assert body.get("provider")
print( { "model": body["model"], "provider": body["provider"], "finish_reason": choice["finish_reason"], "usage": body.get("usage"), })OpenRouter’s quickstart documents the base URL, Bearer authentication and optional attribution headers. The script adds application checks that the generic quickstart cannot know: exact content, finish reason, reported model, selected provider and usage.
reasoning.effort: "none" kept all three successful acceptance calls at zero
reported reasoning tokens. That is useful for a mechanically checkable marker.
It is not a recommendation for hard reasoning tasks. See the
GLM-5.2 reasoning-effort map before
raising effort and output budgets.
Inspect the live endpoint catalog
Section titled “Inspect the live endpoint catalog”Check availability before a paid call:
curl -fsS \ "https://openrouter.ai/api/v1/models/z-ai/glm-5.2/endpoints" | jq '{ id: .data.id, endpoints: [ .data.endpoints[] | { tag, provider_name, status, quantization, context_length, max_completion_tokens, supported_parameters, pricing } ] }'Our dated snapshot contained:
| Catalog field | July 30 observation |
|---|---|
| Endpoints / named providers | 33 / 29 |
| Context minimum / median / maximum | 96,890 / 1,048,576 / 1,048,576 |
| Endpoints at or above one million context | 24 |
| FP4 / FP8 / unspecified precision | 9 / 16 / 8 |
tools / tool_choice declared |
33 / 32 |
| structured outputs declared | 27 |
parallel_tool_calls declared |
1 |
| reasoning effort declared | 33 |
Those differences are operational. A one-million-token prompt can fail if
the selected endpoint advertises only 96,890 tokens. A request with
parallel_tool_calls has a much smaller eligible pool than a request with one
tool. require_parameters: true asks the router to exclude endpoints that do
not declare the fields you sent; it does not convert catalog metadata into a
guaranteed successful call.
The endpoint feed also contained a maximum-completion outlier of 1,048,576. Do not turn that field into a model claim. Z.ai’s first-party page says 128K maximum output. Until a selected provider contract and a bounded test prove otherwise, cap output at the smaller verified limit.
Choose dynamic, ordered or pinned routing
Section titled “Choose dynamic, ordered or pinned routing”OpenRouter’s provider-routing reference defines three useful operating modes.
Let the router optimize
Section titled “Let the router optimize”{ "provider": { "sort": "latency", "allow_fallbacks": true, "require_parameters": true, "data_collection": "deny" }}Use this for prototypes and workloads where availability matters more than a
fixed backend. Record response.provider, token usage and cost on every call.
Do not assume the next call will select the same provider.
Prefer one endpoint but retain fallback
Section titled “Prefer one endpoint but retain fallback”{ "provider": { "order": ["novita/fp8"], "allow_fallbacks": true, "require_parameters": true, "data_collection": "deny" }}This expresses a preference, not a pin. If the named endpoint cannot serve the request, OpenRouter may continue through its remaining eligible list. That is often the correct production compromise when the application can tolerate provider drift.
Pin one exact endpoint and fail closed
Section titled “Pin one exact endpoint and fail closed”{ "provider": { "only": ["novita/fp8"], "allow_fallbacks": false, "require_parameters": true, "data_collection": "deny" }}Use the full provider/variant slug when precision or an endpoint-specific
contract matters. Use a base slug such as novita when every variant from
that provider is acceptable. Failing closed trades uptime for reproducibility:
the application receives an error instead of silently moving to another
backend.
Read the Docker receipts
Section titled “Read the Docker receipts”
Actual evidence view generated from the sanitized archive. It contains no API key, Authorization header, cookie, response ID, tool-call ID, hidden reasoning, raw provider error message, balance or account metadata.
| Case | Provider policy | Result | Time | Reported cost |
|---|---|---|---|---|
| Dynamic text | latency sort, fallback on | Ionstream; exact marker | 7.920 s | $0.0000516 |
| Pinned text | only novita/fp8 |
Novita; exact marker | 1.826 s | $0.0000313472 |
| Dynamic tool | latency sort, fallback on | Ionstream; exact call | 1.338 s | $0.00017768 |
| Pinned tool | only novita/fp8 |
HTTP 404; no eligible endpoint | 0.581 s | not reported |
| Invalid provider | only nonexistent slug | expected HTTP 404 | 0.600 s | not reported |
| Invalid model | nonexistent model ID | expected HTTP 400 | 1.341 s | not reported |
The values are single observations. They do not establish latency percentiles, uptime, output quality or a winner. The useful result is structural: dynamic routing could satisfy the forced tool call, while the exact endpoint could satisfy the text call but not that tool request at the same test time.
The public endpoint record for novita/fp8 declared tools and
tool_choice. The pinned request still produced a classified
no-endpoint/provider/tool error. That discrepancy is why production
acceptance must send the real combination of model, tool schema, routing
policy and privacy fields. Catalog inspection alone is insufficient.
Download the sanitized JSON receipt.
Validate a tool call, not just HTTP 200
Section titled “Validate a tool call, not just HTTP 200”The successful dynamic request forced one synthetic function:
{ "tools": [ { "type": "function", "function": { "name": "lookup_ticket", "description": "Read one synthetic routing fixture.", "parameters": { "type": "object", "properties": { "ticket_id": { "type": "string", "description": "Exact synthetic ticket identifier." } }, "required": ["ticket_id"], "additionalProperties": false } } } ], "tool_choice": { "type": "function", "function": {"name": "lookup_ticket"} }}The application accepted only:
{ "finish_reason": "tool_calls", "provider": "Ionstream", "tool": { "name": "lookup_ticket", "arguments": {"ticket_id": "ROUTE-730"} }}That response proves tool selection and argument serialization. It does not prove execution. Your application must parse the argument string, reject unknown keys, validate types and business rules, authorize the action, execute it with time and spend limits, append the matching tool result, and validate the model’s final answer. The complete GLM-5.2 tool loop covers that second half.
OpenRouter’s tool guide also says tool definitions belong in the follow-up request so the router can validate the schema again. Keep reasoning details and the assistant tool-call message intact when the selected model requires them.
Reconstruct the selected-route cost
Section titled “Reconstruct the selected-route cost”The top-level catalog snapshot displayed $0.6944/M input,
$0.12896/M cached input and $2.1824/M output. Those were discounted,
changing gateway values, not a universal backend rate. The same day’s public
model page displayed another price, reinforcing the need to date every quote.
The dynamic text receipt lets us infer the actual selected-route arithmetic:
18 input × $1.40 / 1M = $0.0000252 6 output × $4.40 / 1M = $0.0000264reported total $0.0000516The pinned Novita text receipt matched its catalog entry:
20 input × $0.6944 / 1M = $0.0000138880 8 output × $2.1824 / 1M = $0.0000174592reported total $0.0000313472For a hypothetical 100,000 uncached input and 10,000 output tokens:
pinned snapshot = 100,000 × $0.6944/M + 10,000 × $2.1824/M = $0.091264
dynamic text rate = 100,000 × $1.40/M + 10,000 × $4.40/M = $0.184000
difference = $0.092736, or 101.61% above the lower routeThis does not say the pinned route is always cheaper. Dynamic routing can
change backend, price and discount. Credit-purchase fees, taxes, retries,
cache eligibility and future promotions are also outside the arithmetic. Use
the response’s usage.cost for per-call telemetry and the
GLM-5.2 cost calculator for workload
scenarios.
Apply privacy filters deliberately
Section titled “Apply privacy filters deliberately”The live tests sent:
{ "data_collection": "deny"}OpenRouter’s routing docs describe that as a filter for providers that may store or use request data. Its data-collection page says gateway prompt/response logging and product-improvement use are opt-in, while request metadata such as token counts and latency remains stored. Downstream providers still have their own policies.
For a stricter retention requirement, OpenRouter exposes:
{ "data_collection": "deny", "zdr": true}We did not send that stronger combination in this run. Test it with your actual model parameters because it can shrink the eligible endpoint pool. The ZDR documentation also warns that inference routing controls do not automatically cover optional third-party plugins or server tools. A web-search plugin can have a different processor and retention path from the selected model endpoint.
Never log the API key, Authorization header, prompt, raw provider error body or hidden reasoning by default. Keep only the fields needed to explain an incident: internal request correlation, selected model and provider, route policy version, status, finish reason, token counts, cost, retry count and a redacted error category.
Troubleshoot by failure layer
Section titled “Troubleshoot by failure layer”| Symptom | Likely layer | Safe next check |
|---|---|---|
| HTTP 401 | Missing, invalid or disabled OpenRouter key | Check variable presence without printing it; do not send a Z.ai key |
| HTTP 402 | Insufficient account credit | Stop automatic retries and require a budget decision |
| HTTP 400 for model | Wrong slug or invalid field | Use exactly z-ai/glm-5.2; compare the request with /models |
HTTP 404 after only |
Invalid provider or no route satisfying that exact policy | Inspect the full endpoint slug, tool fields and privacy filters; do not silently remove the pin |
| HTTP 429 | Gateway or route rate limit | Honor Retry-After, add jitter and cap retries |
| HTTP 502/503 | Provider failure or no eligible provider | Preserve policy evidence; retry only when the operation is idempotent |
Empty content with length |
Reasoning or output consumed the cap | Inspect reasoning-token usage and raise a bounded output budget |
| Tool works dynamically but fails pinned | Selected endpoint cannot satisfy the full request now | Decide whether to allow fallback or fail closed; do not call the tool without validation |
| Long prompt fails below one million | Chosen endpoint has a smaller context contract | Read endpoint metadata, count tokens and reserve output/tool overhead |
| Parallel tools fail | Very small eligible pool | Set parallel_tool_calls: false or pin the one verified route |
OpenRouter’s
error reference
documents standard status classes and Retry-After handling. Our invalid
model returned 400, while invalid and currently unsatisfied exact-provider
policies returned 404. Treat the actual status plus a sanitized error
category as evidence; do not retry every non-200 as if it were transient.
The LangChain route test documents another failure shape: reasoning consumed a small output cap and left no final text. A gateway response can be schema-valid and still fail the application predicate.
Promote a route only after acceptance tests
Section titled “Promote a route only after acceptance tests”Use a versioned contract for every production route:
model: z-ai/glm-5.2endpoint_catalog_checked_at: 2026-07-30T02:42:33+08:00provider_policy: mode: dynamic sort: latency allow_fallbacks: true require_parameters: true data_collection: denyreasoning: effort: nonerequired_checks: exact_text: pass exact_tool_schema: pass selected_provider_recorded: pass finish_reason_checked: pass usage_and_cost_recorded: passnegative_controls: invalid_model_rejected: pass invalid_provider_rejected: passlimits: timeout_seconds: 60 retries: 0rollback: trigger: route, cost, schema, privacy, or latency gate failsThe production version should add a representative prompt set, p50/p90 latency, tool-call validity rate, context limits, maximum accepted cost, privacy requirements and an explicit fallback decision. Freeze the route policy beside application code. A dashboard setting changed outside source control can otherwise alter behavior without a deploy.
Run the same suite after model, SDK, tool schema, provider order, privacy setting or endpoint revision changes. One successful marker validates connectivity; a release gate validates the user task.
Answer practical OpenRouter questions
Section titled “Answer practical OpenRouter questions”Is OpenRouter the official Z.ai API?
Section titled “Is OpenRouter the official Z.ai API?”No. OpenRouter is a third-party gateway that can route GLM-5.2 to multiple providers. Z.ai publishes the model and a separate first-party API. Compare the routes in the GLM-5.2 API provider guide.
What model ID should I use?
Section titled “What model ID should I use?”Use z-ai/glm-5.2 with https://openrouter.ai/api/v1. The first-party Z.ai
model ID is glm-5.2. Do not mix the two namespaces.
Can I use the OpenAI SDK?
Section titled “Can I use the OpenAI SDK?”Yes. OpenRouter’s quickstart documents pointing the OpenAI SDK at its base URL. Still pass the provider policy as an extra request body and verify that your SDK preserves it on the wire.
Does require_parameters: true guarantee tool calling?
Section titled “Does require_parameters: true guarantee tool calling?”No. It filters against declared endpoint support. Our exact endpoint advertised both tool fields, yet the forced pinned request returned a no-eligible-endpoint 404 while dynamic routing succeeded. Keep a live tool acceptance test.
Should I enable fallbacks?
Section titled “Should I enable fallbacks?”Enable them when availability matters and your application can tolerate a different backend after recording and validating it. Disable them when precision, processor, region, privacy or reproducibility requires fail-closed behavior.
Can I use a Z.ai Coding Plan key?
Section titled “Can I use a Z.ai Coding Plan key?”No. OpenRouter uses an OpenRouter key. Coding Plan credentials and quota belong to Z.ai’s supported coding-tool routes, not a general gateway.
Does one million context apply to every route?
Section titled “Does one million context apply to every route?”No. The snapshot ranged from 96,890 to 1,048,576 context tokens, with 24 of 33 endpoints at or above one million. Reserve output and tool-schema overhead inside the selected endpoint’s limit.
Why did the pinned tool call fail if text worked?
Section titled “Why did the pinned tool call fail if text worked?”The full eligibility calculation includes model, endpoint, provider policy, privacy fields, tool schema and requested parameters. Public metadata is a dated declaration, not proof that the combined request is currently routable. The 404 is a reason to choose fallback or stop—not to remove safety fields blindly.
Audit the sources and method
Section titled “Audit the sources and method”Primary sources checked July 30, 2026:
- OpenRouter GLM-5.2 model/API view;
- OpenRouter quickstart;
- provider routing and endpoint pinning;
- public model catalog and GLM-5.2 endpoint catalog;
- provider registry;
- tool-calling guide;
- data-collection controls and zero data retention;
- errors and retry behavior;
- Z.ai GLM-5.2 guide;
- official GLM-5.2 weights.
We also screened the current AI HOT feed as an untrusted discovery source. Its eight new items did not establish a direct GLM-5.2 routing claim, so no news claim was transferred into this article.
The functional evidence came from a digest-pinned, outbound-only Docker
container. The host’s known bridge-DNS failure was reproduced before using
the approved host-network exception. Three successful paid calls, one pinned
route boundary, two negative controls, two public catalogs and one provider
registry request were archived after removing credentials and identifiers.
The separate Verify LLM API scan returned CONSISTENT behavioral evidence
but failed all six short output constraints; it is not identity proof or the
deployment acceptance result.
The test did not run Z.ai direct, Novita direct, Coding Plan, a one-million token prompt, parallel tools, ZDR, streaming, load, quality evaluation or latency percentiles. Prices, discounts, endpoint counts and provider status can change after the check date.
