Client or integration
Other — OpenCodex provider UI / provider quota probe (zai provider)
Provider or upstream service
Z.ai — GLM Coding Plan
OpenCodex version
2.49.0
Endpoint or capability
Provider quota probe (GET https://api.z.ai/api/monitor/usage/quota/limit) vs. Anthropic-compatible messages endpoint POST https://api.z.ai/api/anthropic/v1/messages
Current behaviour
fetchZaiQuota() only runs when isCanonicalZaiBaseUrl() matches. The canonical list accepts https://api.z.ai and https://api.z.ai/api/coding/paas/v4, but not the documented Anthropic-compatible coding endpoint https://api.z.ai/api/anthropic.
Z.ai's GLM Coding Plan serves the Anthropic wire format only at https://api.z.ai/api/anthropic:
POST https://api.z.ai/api/anthropic/v1/messages → 200 (works)
POST https://api.z.ai/v1/messages → 404
POST https://api.z.ai/api/coding/paas/v4/v1/messages → 404
POST https://api.z.ai/api/coding/paas/v4/messages → 404
Since the Anthropic adapter builds the messages URL as {baseUrl}/v1/messages, users face a mutually exclusive choice:
baseUrl = https://api.z.ai/api/anthropic → messages work, provider quota view is empty
baseUrl = https://api.z.ai/api/coding/paas/v4 → quota view works, all message requests 404
Expected behaviour
The quota probe should also treat https://api.z.ai/api/anthropic as a canonical Z.ai Coding Plan base URL, so a provider configured for the Anthropic wire format still gets its quota/usage view populated.
Minimal redacted request or reproduction
# 1. Configure a provider with:
# adapter = anthropic
# baseUrl = https://api.z.ai/api/anthropic
# authMode = key (x-api-key)
#
# 2. Verify messages work through the proxy — 200 OK.
# 3. Open the provider "Usage"/quota view — it stays empty.
# The probe's guard rejects the URL:
# src/providers/quota.ts :: isCanonicalZaiBaseUrl("https://api.z.ai/api/anthropic") === false
# → fetchZaiQuota() returns null before any request is made.
# Messages endpoint itself is healthy (key redacted):
curl -s -o /dev/null -w '%{http_code}\n' https://api.z.ai/api/anthropic/v1/messages \
-H 'x-api-key: <REDACTED>' -H 'anthropic-version: 2023-06-01' \
-H 'Content-Type: application/json' \
-d '{"model":"glm-5.3-flash","max_tokens":8,"messages":[{"role":"user","content":"hi"}]}'
# → 200
Actual response or error
No error is surfaced — the quota view silently stays empty, because fetchZaiQuota bails out at the canonical-URL guard before requesting https://api.z.ai/api/monitor/usage/quota/limit.
For reference, the quota endpoint itself works fine for Coding Plan keys (verified with Authorization: Bearer <coding-plan-key> → 200 with a data.limits[] payload), it is only the canonical-URL gate that excludes the Anthropic base URL.
Upstream documentation
- Z.ai GLM Coding Plan / DevPack docs: https://docs.z.ai/devpack/overview — the Anthropic-compatible base URL for coding tools is
https://api.z.ai/api/anthropic (e.g. used as ANTHROPIC_BASE_URL for Claude Code).
- Quota monitor endpoint used by the probe:
GET https://api.z.ai/api/monitor/usage/quota/limit (no public spec; verified empirically, returns {code, data:{limits:[{type,unit,number,usage,currentValue,remaining,percentage,nextResetTime}]}}).
Suggested mapping or implementation notes
One-line addition in isCanonicalZaiBaseUrl (src/providers/quota.ts):
|| normalized === `${ZAI_BASE_URL}/api/anthropic`
fetchZaiQuota already derives the monitor host correctly for the international host and sends Authorization: Bearer <key>, which the monitor endpoint accepts for Coding Plan keys.
Additional data point from a controlled A/B test (30 identical requests ≈ 75k tokens per leg, GLM-5.3-Flash, inside the September 2026 Flash campaign window, neutral user-agent): the OpenAI-compatible path (/api/coding/paas/v4/chat/completions) reduced the plan's remaining budget by ~31 units per window vs ~6 units for the Anthropic path (/api/anthropic/v1/messages) — i.e. the two paths account quota noticeably differently, which makes a working quota view for the Anthropic base URL even more useful.
Additional context and attachments
Configured provider (redacted):
{
"name": "zai",
"adapter": "anthropic",
"baseUrl": "https://api.z.ai/api/anthropic",
"defaultModel": "glm-5.3-flash",
"authMode": "key",
"apiKeyTransport": "x-api-key"
}
Happy to test a fix against 2.49.x.
Checks
Client or integration
Other — OpenCodex provider UI / provider quota probe (zai provider)
Provider or upstream service
Z.ai — GLM Coding Plan
OpenCodex version
2.49.0
Endpoint or capability
Provider quota probe (
GET https://api.z.ai/api/monitor/usage/quota/limit) vs. Anthropic-compatible messages endpointPOST https://api.z.ai/api/anthropic/v1/messagesCurrent behaviour
fetchZaiQuota()only runs whenisCanonicalZaiBaseUrl()matches. The canonical list acceptshttps://api.z.aiandhttps://api.z.ai/api/coding/paas/v4, but not the documented Anthropic-compatible coding endpointhttps://api.z.ai/api/anthropic.Z.ai's GLM Coding Plan serves the Anthropic wire format only at
https://api.z.ai/api/anthropic:POST https://api.z.ai/api/anthropic/v1/messages→ 200 (works)POST https://api.z.ai/v1/messages→ 404POST https://api.z.ai/api/coding/paas/v4/v1/messages→ 404POST https://api.z.ai/api/coding/paas/v4/messages→ 404Since the Anthropic adapter builds the messages URL as
{baseUrl}/v1/messages, users face a mutually exclusive choice:baseUrl = https://api.z.ai/api/anthropic→ messages work, provider quota view is emptybaseUrl = https://api.z.ai/api/coding/paas/v4→ quota view works, all message requests 404Expected behaviour
The quota probe should also treat
https://api.z.ai/api/anthropicas a canonical Z.ai Coding Plan base URL, so a provider configured for the Anthropic wire format still gets its quota/usage view populated.Minimal redacted request or reproduction
Actual response or error
No error is surfaced — the quota view silently stays empty, because
fetchZaiQuotabails out at the canonical-URL guard before requestinghttps://api.z.ai/api/monitor/usage/quota/limit.For reference, the quota endpoint itself works fine for Coding Plan keys (verified with
Authorization: Bearer <coding-plan-key>→ 200 with adata.limits[]payload), it is only the canonical-URL gate that excludes the Anthropic base URL.Upstream documentation
https://api.z.ai/api/anthropic(e.g. used asANTHROPIC_BASE_URLfor Claude Code).GET https://api.z.ai/api/monitor/usage/quota/limit(no public spec; verified empirically, returns{code, data:{limits:[{type,unit,number,usage,currentValue,remaining,percentage,nextResetTime}]}}).Suggested mapping or implementation notes
One-line addition in
isCanonicalZaiBaseUrl(src/providers/quota.ts):fetchZaiQuotaalready derives the monitor host correctly for the international host and sendsAuthorization: Bearer <key>, which the monitor endpoint accepts for Coding Plan keys.Additional data point from a controlled A/B test (30 identical requests ≈ 75k tokens per leg, GLM-5.3-Flash, inside the September 2026 Flash campaign window, neutral user-agent): the OpenAI-compatible path (
/api/coding/paas/v4/chat/completions) reduced the plan'sremainingbudget by ~31 units per window vs ~6 units for the Anthropic path (/api/anthropic/v1/messages) — i.e. the two paths account quota noticeably differently, which makes a working quota view for the Anthropic base URL even more useful.Additional context and attachments
Configured provider (redacted):
{ "name": "zai", "adapter": "anthropic", "baseUrl": "https://api.z.ai/api/anthropic", "defaultModel": "glm-5.3-flash", "authMode": "key", "apiKeyTransport": "x-api-key" }Happy to test a fix against 2.49.x.
Checks