diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7a3962a26..b46b726f6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Added
+
+- **Requesty provider.** `requesty` is a new OpenAI-compatible registry row
+ (`https://router.requesty.ai/v1`, key from `REQUESTY_API_KEY`). Its model
+ list is discovered from Requesty's managed models endpoint, falling back to
+ the full `/models` catalog.
+
## [1.7.0] - 2026-09-23
### Added
diff --git a/README.md b/README.md
index ec850f094..0d4d3b8e7 100644
--- a/README.md
+++ b/README.md
@@ -406,10 +406,10 @@ providers = [
"nvidia-nim", "atlascloud", "wanjie-ark", "volcengine", "xiaomi-mimo",
"novita", "fireworks", "siliconflow", "siliconflow-cn", "arcee", "moonshot",
"huggingface", "together", "stepfun", "deepinfra", "meta",
- "groq", "cerebras", "baseten", "xai",
+ "groq", "cerebras", "baseten", "xai", "requesty",
# Local servers (no API key required)
"ollama", "vllm", "sglang",
-] # 30 providers; aliases like `nim`, `kimi`, `hf`, `grok` resolve automatically
+] # 31 providers; aliases like `nim`, `kimi`, `hf`, `grok` resolve automatically
```
Any new OpenAI-compatible vendor is a one-row addition to
diff --git a/docs/guide/SETUP_GUIDE.md b/docs/guide/SETUP_GUIDE.md
index b0ce2e05f..08c871169 100644
--- a/docs/guide/SETUP_GUIDE.md
+++ b/docs/guide/SETUP_GUIDE.md
@@ -98,11 +98,14 @@ Create `~/.clawcodex/config.json` (only the providers you actually use are requi
| `minimax` | `https://api.minimaxi.com/anthropic` | `MiniMax-M2.7` |
| `openrouter` | `https://openrouter.ai/api/v1` | `deepseek/deepseek-v4-pro` |
| `deepseek` | `https://api.deepseek.com` | `deepseek-flash` |
+| `requesty` | `https://router.requesty.ai/v1` | `anthropic/claude-sonnet-4-5` (also `openai/gpt-4o-mini`) |
> **DeepSeek:** `deepseek-flash` is DeepSeek-V4.1-Flash — 1M context, 384K max output, thinking on by default, and the first DeepSeek model that accepts images. `deepseek-v4-pro` is being retired: the id still works, but from 2026-09-14 its requests run V4.1 Flash and bill at the Flash price. The older spellings (`deepseek-v4-flash`, `deepseek-chat`, `deepseek-reasoner`) still resolve.
> **Z.ai (GLM):** clawcodex uses Z.ai's OpenAI-compatible GLM Coding Plan at `https://api.z.ai/api/coding/paas/v4`, serving `GLM-5.1` (stable) and `GLM-5.2` (preview). The legacy provider name `glm` is still accepted as an alias for `zai`. Get a key at .
+> **Requesty:** an OpenAI-compatible gateway to many vendors. Use a catalog id such as `openai/gpt-4o-mini` or a managed model id such as `claude-sonnet-5`. Put the key in the provider's `api_key`, or export `REQUESTY_API_KEY` (get one at ). For EU routing set the provider's `base_url` to `https://router.eu.requesty.ai/v1`. Docs: .
+
## 4. Run
```bash
diff --git a/src/providers/model_discovery.py b/src/providers/model_discovery.py
index f81783a7c..8f3faf43f 100644
--- a/src/providers/model_discovery.py
+++ b/src/providers/model_discovery.py
@@ -129,6 +129,30 @@ def fetch_openai_compatible_models(
return None
+def fetch_requesty_models(
+ base_url: str, api_key: str | None = None, *, timeout: float = FETCH_TIMEOUT_S,
+) -> list[str] | None:
+ """GET {base}/models/managed → chat data[].id, else the {base}/models
+ catalog. Requesty's managed list is its curated set of routing policies,
+ so it is preferred; the full catalog is the fallback. None on failure."""
+ try:
+ payload = _http_get_json(
+ base_url.rstrip("/") + "/models/managed", api_key=api_key, timeout=timeout,
+ )
+ data = payload.get("data") if isinstance(payload, dict) else None
+ if isinstance(data, list):
+ models = [
+ str(item["id"]) for item in data
+ if isinstance(item, dict) and item.get("id")
+ and item.get("api", "chat") == "chat"
+ ]
+ if models:
+ return models
+ except Exception: # noqa: BLE001
+ logger.debug("model discovery: requesty managed fetch failed for %s", base_url, exc_info=True)
+ return fetch_openai_compatible_models(base_url, api_key, timeout=timeout)
+
+
def fetch_ollama_models(
base_url: str, *, timeout: float = FETCH_TIMEOUT_S,
) -> list[str] | None:
@@ -160,6 +184,8 @@ def _fetch_for_kind(
return fetch_ollama_models(base_url, timeout=timeout)
if kind == "openai-compatible":
return fetch_openai_compatible_models(base_url, api_key, timeout=timeout)
+ if kind == "requesty":
+ return fetch_requesty_models(base_url, api_key, timeout=timeout)
return None
diff --git a/src/providers/openai_compatible_specs.py b/src/providers/openai_compatible_specs.py
index 174ebf7be..df74e45bd 100644
--- a/src/providers/openai_compatible_specs.py
+++ b/src/providers/openai_compatible_specs.py
@@ -476,6 +476,42 @@ def resolved_class_name(self) -> str:
env_vars=("XAI_API_KEY", "GROK_API_KEY"),
aliases=("x-ai", "x_ai", "grok"),
),
+ # Requesty, a multi-vendor gateway like OpenRouter. Model ids are either
+ # catalog ids in ``vendor/model`` form (``anthropic/claude-sonnet-4-5``,
+ # the same default OpenRouter uses) or Requesty's managed routing
+ # policies (short ids such as ``claude-sonnet-5``, listed at
+ # ``GET /models/managed``). The rest of the curated list is seeded from
+ # the managed ids; discovery (the ``requesty`` catalog kind) appends the
+ # live managed list and falls back to the full ``/models`` catalog. EU
+ # routing is available at https://router.eu.requesty.ai/v1 via the
+ # provider's ``base_url``.
+ ProviderSpec(
+ id="requesty",
+ dynamic_catalog="requesty",
+ catalog_mode="hybrid",
+ label="Requesty (multi-vendor gateway)",
+ default_base_url="https://router.requesty.ai/v1",
+ default_model="anthropic/claude-sonnet-4-5",
+ available_models=(
+ "anthropic/claude-sonnet-4-5",
+ "claude-sonnet-5",
+ "claude-opus-5",
+ "claude-haiku-4-5",
+ "gpt-5.6-sol",
+ "gpt-5.6-terra",
+ "gpt-5.6-luna",
+ "gpt-5.5",
+ "gpt-5.4",
+ "gpt-5.4-mini",
+ "gpt-5.3-codex",
+ "deepseek-v4.1-flash",
+ "gemini-3.5-flash",
+ "kimi-k3",
+ "glm-5.2",
+ "grok-4.5",
+ ),
+ env_vars=("REQUESTY_API_KEY",),
+ ),
)
diff --git a/tests/providers/test_model_discovery.py b/tests/providers/test_model_discovery.py
index 843598a46..e60979cda 100644
--- a/tests/providers/test_model_discovery.py
+++ b/tests/providers/test_model_discovery.py
@@ -65,6 +65,42 @@ def _boom(url, *, api_key, timeout):
monkeypatch.setattr(md, "_http_get_json", _boom)
assert md.fetch_openai_compatible_models("http://x/v1") is None
assert md.fetch_ollama_models("http://x/v1") is None
+ assert md.fetch_requesty_models("http://x/v1") is None
+
+
+def test_requesty_parser_prefers_managed_chat_models(monkeypatch: pytest.MonkeyPatch) -> None:
+ seen: list[str] = []
+
+ def _fake(url, *, api_key, timeout):
+ seen.append(url)
+ return {"data": [
+ {"id": "claude-sonnet-4-5", "api": "chat"},
+ {"id": "gpt-5.4-mini@eu", "api": "chat"},
+ {"id": "text-embedding-3-small", "api": "embedding"},
+ ]}
+
+ monkeypatch.setattr(md, "_http_get_json", _fake)
+ models = md.fetch_requesty_models("https://router.requesty.ai/v1/")
+ assert models == ["claude-sonnet-4-5", "gpt-5.4-mini@eu"]
+ assert seen == ["https://router.requesty.ai/v1/models/managed"]
+
+
+def test_requesty_parser_falls_back_to_full_catalog(monkeypatch: pytest.MonkeyPatch) -> None:
+ seen: list[str] = []
+
+ def _fake(url, *, api_key, timeout):
+ seen.append(url)
+ if url.endswith("/models/managed"):
+ raise OSError("unavailable")
+ return {"data": [{"id": "openai/gpt-4o-mini"}]}
+
+ monkeypatch.setattr(md, "_http_get_json", _fake)
+ models = md.fetch_requesty_models("https://router.requesty.ai/v1")
+ assert models == ["openai/gpt-4o-mini"]
+ assert seen == [
+ "https://router.requesty.ai/v1/models/managed",
+ "https://router.requesty.ai/v1/models",
+ ]
# ---------------------------------------------------------------------------
diff --git a/tests/test_opencode_compat_providers.py b/tests/test_opencode_compat_providers.py
index 5ec53a69d..f3d92804f 100644
--- a/tests/test_opencode_compat_providers.py
+++ b/tests/test_opencode_compat_providers.py
@@ -28,7 +28,8 @@
# kimi-k3 was registered, so it belongs to exactly the population these
# parameterized guards describe — curated list leads, discovery appends,
# vendor env var sources the key.
-PORTED = ("groq", "cerebras", "baseten", "xai", "moonshot")
+# ``requesty`` is a hosted hybrid gateway row for the same reason.
+PORTED = ("groq", "cerebras", "baseten", "xai", "moonshot", "requesty")
@pytest.mark.parametrize("provider_id", PORTED)
@@ -102,6 +103,7 @@ def test_the_key_is_sourced_from_the_vendor_env_var(provider_id: str) -> None:
"baseten": "BASETEN_API_KEY",
"xai": "XAI_API_KEY",
"moonshot": "MOONSHOT_API_KEY",
+ "requesty": "REQUESTY_API_KEY",
}[provider_id]
assert expected in SPECS_BY_ID[provider_id].env_vars
diff --git a/tests/test_provider_registry.py b/tests/test_provider_registry.py
index a820f73e0..9e9cb5c37 100644
--- a/tests/test_provider_registry.py
+++ b/tests/test_provider_registry.py
@@ -53,6 +53,7 @@
"cerebras",
"baseten",
"xai",
+ "requesty",
}
# A sample of (id -> (base_url, default_model)) — each vendor's published
@@ -73,6 +74,7 @@
"cerebras": ("https://api.cerebras.ai/v1", "gpt-oss-120b"),
"baseten": ("https://inference.baseten.co/v1", "deepseek-ai/DeepSeek-V4-Pro"),
"xai": ("https://api.x.ai/v1", "grok-4.5"),
+ "requesty": ("https://router.requesty.ai/v1", "anthropic/claude-sonnet-4-5"),
}
diff --git a/ui-desktop/src/app/settings/constants.ts b/ui-desktop/src/app/settings/constants.ts
index c95c48e76..d4a59523c 100644
--- a/ui-desktop/src/app/settings/constants.ts
+++ b/ui-desktop/src/app/settings/constants.ts
@@ -220,6 +220,13 @@ export const PROVIDER_GROUPS: ProviderPrefix[] = [
description: 'Authenticate via AWS profile + region',
docsUrl: 'https://docs.aws.amazon.com/bedrock/latest/userguide/bedrock-regions.html',
priority: 23
+ },
+ {
+ prefix: 'REQUESTY_',
+ name: 'Requesty',
+ description: 'OpenAI-compatible gateway to many vendors',
+ docsUrl: 'https://app.requesty.ai/api-keys',
+ priority: 24
}
]
diff --git a/vscode-extension/clawcodex-vscode/src/state.js b/vscode-extension/clawcodex-vscode/src/state.js
index 8a16bcc67..01422f006 100644
--- a/vscode-extension/clawcodex-vscode/src/state.js
+++ b/vscode-extension/clawcodex-vscode/src/state.js
@@ -12,6 +12,7 @@ const PROVIDER_LABELS = {
zai: 'Z.ai',
minimax: 'MiniMax',
openrouter: 'OpenRouter',
+ requesty: 'Requesty',
moonshot: 'Moonshot',
ollama: 'Ollama',
together: 'Together AI',
@@ -36,6 +37,7 @@ const PROVIDER_ENV_VARS = {
zai: ['ZAI_API_KEY', 'Z_AI_API_KEY'],
minimax: ['MINIMAX_API_KEY'],
openrouter: ['OPENROUTER_API_KEY'],
+ requesty: ['REQUESTY_API_KEY'],
moonshot: ['MOONSHOT_API_KEY'],
together: ['TOGETHER_API_KEY'],
fireworks: ['FIREWORKS_API_KEY'],