web2api-proxy
v1.0.0
Published
Universal OpenAI-compatible reverse proxy for web chat interfaces (FreeModels.Pro) with agent tool calling support
Maintainers
Readme
Web2API
Overview • Features • Quick Start • Models • Clients • Architecture • Guides • Disclaimer • License
Table of Contents
- What is Web2API?
- Key Features
- Quick Start
- Model Catalog & Aliases
- Client Integration
- Architecture & Structure
- Advanced Guides
- Development & Testing
- Disclaimer & Legal Notice
- License
What is Web2API?
Web chat platforms (such as FreeModels.Pro) provide free access to high-end frontier models like Claude Sonnet 5, Claude Fable 5, GPT 5.6 Sol, GPT 5.6 Terra, GLM 5.2, and Kimi K3. However, these platforms do not offer developer APIs or standard OAuth client credentials.
Modern coding agents like Claude Code, Codex, Cursor, Antigravity, OpenCode, etc require an OpenAI-compatible API exposing /v1/chat/completions with support for structured JSON tool/function calling.
Web2API bridges this gap:
- It acts as an intermediary reverse proxy that intercepts requests from coding agents, translates tool specifications into strict plain-text system directives, and forwards requests to the target web backend (
https://freemodels-chat.freemodels.workers.dev/). - It parses and sanitizes raw model output, stripping markdown code fences (
```json ... ```) and conversational preambles to reliably construct OpenAItool_callspayloads. - It supports full Server-Sent Events (SSE) streaming with real-time text delivery,
reasoning_content(thinking trace) forwarding, and smart tool buffering. - It features a dual-runtime architecture: run locally as a zero-dependency Node.js Daemon (
http://localhost:8000) or deploy as a standalone Cloudflare Worker (worker.js).
Disclaimer: This tool is provided strictly for personal educational and interoperability testing purposes. See Disclaimer & Legal Notice.
Key Features
- Unlock Free Web Models for Agents: Access
claude-sonnet-5,claude-fable-5,sol(GPT 5.6 Sol),terra(GPT 5.6 Terra),glm-5.2, andkimi-k3directly from coding agents. - Dual Operational Modes:
- Local Daemon: Runs on
http://localhost:8000via@hono/node-serverwith instant NPX execution or global CLI install. - Cloudflare Worker: Standalone 82 KB bundle (
worker.js) ready for copy-paste into the Cloudflare Dashboard or deployment via Wrangler.
- Local Daemon: Runs on
- Smart Tool Coercion: Converts OpenAI
toolsdefinitions into strict system instructions, enforcing pure JSON output across web models that lack native function-calling APIs. - Resilient JSON Extractor: Strips markdown fences, extracts outer
{...}or[...]blocks via balanced-brace parsing, and repairs unescaped newlines in diffs. - Streaming SSE with Reasoning Delta: Preserves thinking traces in
delta.reasoning_contentfor real-time rendering in assistants like MiMoCode and Cline. - Upstream Error Normalization: Detects 429 key exhaustion responses (
All providers exhausted. Retry in 20s...) and wraps them in OpenAI-standard error envelopes withRetry-Afterheaders. - Safe & Resource-Bounded:
- Client disconnect cancellation via
AbortSignal. - Browser header emulation (
Origin,Referer,User-Agent) to prevent upstream filtering.
- Client disconnect cancellation via
Quick Start
Method 1: Local Daemon (NPX / NPM)
Option A: Run Instantly with NPX (Zero Install)
Run the local proxy daemon immediately with no clone or install needed:
npx web2api-proxyOr with custom port and API key:
npx web2api-proxy start --port 8080 --key sk-my-secretTest upstream connectivity and models:
npx web2api-proxy checkOption B: Global CLI Installation
Install globally to make the web2api command available anywhere on your system:
npm install -g web2api-proxyThen run:
web2api start
web2api check
web2api --helpCLI Options & Flags
| Flag | Shorthand | Environment Variable | Default | Description |
|---|---|---|---|---|
| --port | -p | PORT | 8000 | Port for the local server |
| --host | | HOST | 127.0.0.1 | Host address to bind to |
| --key | -k | API_KEY | (None) | Require Bearer API key authentication |
| --upstream | -u | UPSTREAM_URL | https://freemodels-chat... | Target web chat backend URL |
| --model | -m | DEFAULT_MODEL | claude-sonnet-5 | Default model fallback |
| --help | -h | | | Show CLI help and options |
| --version | -v | | | Show installed version |
Option C: Clone and Run from Source
git clone https://github.com/IMROVOID/Web2API.git
cd Web2API
npm install
npm run devYour local endpoint is available at http://localhost:8000/v1. Default API key for all clients is sk-web2api-local (or any string when API_KEY is not set).
Method 2: Cloudflare Workers (Serverless)
Deploy a 24/7 serverless gateway without keeping your computer running.
Step 1: Build the Worker Bundle (Optional)
npm run buildThis compiles the standalone zero-dependency bundle to worker.js in the project root.
Step 2: Create Worker in Cloudflare Dashboard (Primary Method)
- Log into the Cloudflare Dashboard.
- Go to Workers & Pages -> Create application -> Create Worker.
- Set the name to
web2apiand click Deploy. - Click Edit code, select all existing code, delete it, and paste the entire copied contents of
worker.js. - Click Deploy in the top right.
Step 3: Add Variables & Secrets (Optional)
In your Worker, go to Settings -> Variables and Secrets and add:
| Variable | Type | Required | Default | Description |
|---|---|---|---|---|
| API_KEY | Secret | Optional | (None) | Client authorization bearer key (e.g. sk-web2api-local). If unset, access is open. |
| UPSTREAM_URL | Text | Optional | https://freemodels-chat.freemodels.workers.dev | Upstream web chat backend endpoint |
| DEFAULT_MODEL | Text | Optional | claude-sonnet-5 | Default model when unspecified |
Click Save and deploy.
Your Cloudflare Worker API URL:
https://web2api.<your-subdomain>.workers.dev/v1Alternative: Deploy via Wrangler CLI
If you prefer deploying via the command line:
npx wrangler deployModel Catalog & Aliases
Web2API automatically maps requested model aliases to canonical upstream models:
| Request Model Alias | Canonical Upstream Model | Provider | Strengths | Type |
|---|---|---|---|---|
| claude-sonnet-5, claude-3-5-sonnet, claude-3-sonnet, claude-3-opus | claude-sonnet-5 | Anthropic | Thinking, Coding, Analysis | Reasoning |
| sol, gpt-4o-mini, claude-3-5-haiku | sol | OpenAI | High Speed, Conversational | Fast |
| terra, gpt-4o, gpt-4 | terra | OpenAI | Deep Reasoning, Math, Science | Reasoning |
| claude-fable-5 | claude-fable-5 | Anthropic | Creative Writing, Long Form | Creative |
| claude-fable-5.1 | claude-fable-5.1 | Anthropic | Agentic Coding, Deep Research | Creative |
| glm-5.2, glm-4 | glm-5.2 | Z.AI | Bilingual, Tools, Knowledge | Pro |
| kimi-k3, kimi | kimi-k3 | Moonshot AI | 200K+ Context Window, Recall | Long Context |
Underlying Model Reality (Nemotron-3 Super 120B)
[!NOTE] Architecture Reality Check: While FreeModels.Pro labels its chat options with names like "Claude Sonnet 5", "Claude Fable 5", "GPT 5.6 Sol", "GPT 5.6 Terra", "GLM 5.2", and "Kimi K3", network analysis and response inspection confirm that all requests are actually served by a single open-weights model:
nvidia/nemotron-3-super-120b-a12b(hosted via Nvidia NIM / DashScope provider pools).
What Does This Mean?
- Persona Conditioning: The upstream Cloudflare Worker injects system personas instructing the Nemotron model to roleplay as the requested target model. This can be observed directly in the
reasoning_content(thinking trace) chunks where the model references its persona instructions. - Still Completely Usable & Capable: Despite the marketing names, the service is still completely usable and surprisingly capable. With ~120 billion parameters,
nemotron-3-super-120b-a12bis a high-parameter open-weights reasoning model that delivers:- Strong code generation, refactoring, and multi-step logic.
- Native reasoning/thinking traces (
reasoning_content) for transparent chain-of-thought. - High inference throughput and low latency.
- Web2API Bridging: Web2API makes this backend fully compatible with coding agents (MiMoCode, Aider, Claude Code, Cline, Cursor) by handling prompt coercion and JSON extraction so tool calling works reliably regardless of the underlying roleplay persona.
More Info: Full Reverse Engineering & Model Reality Analysis →
Client Integration
Note: For all clients below, replace
http://localhost:8000/v1with your Cloudflare Worker URL (https://web2api.<your-subdomain>.workers.dev/v1) if using serverless deployment. The default API key issk-web2api-local.
Configuration file path:
~/.9router/db.jsonOr configure via Web Dashboard under Providers -> Add Custom Provider:
- Provider Type:
openai - Base URL:
http://localhost:8000/v1 - API Key:
sk-web2api-local - Models:
claude-sonnet-5, terra, sol, glm-5.2, kimi-k3
Configuration entry for ~/.9router/db.json:
{
"providers": [
{
"id": "web2api",
"name": "Web2API",
"type": "openai",
"baseUrl": "http://localhost:8000/v1",
"apiKey": "sk-web2api-local",
"models": [
"claude-sonnet-5",
"terra",
"sol",
"glm-5.2",
"kimi-k3"
]
}
]
}Configuration file path:
.aider.conf.ymlAdd configuration:
openai-api-base: http://localhost:8000/v1
openai-api-key: sk-web2api-local
model: openai/claude-sonnet-5Or run via CLI:
aider --openai-api-base http://localhost:8000/v1 \
--openai-api-key sk-web2api-local \
--model openai/claude-sonnet-5Configuration file path:
~/.gemini/antigravity/antigravity.jsonAdd configuration:
{
"modelProviders": {
"web2api": {
"type": "openai",
"baseUrl": "http://localhost:8000/v1",
"apiKey": "sk-web2api-local",
"defaultModel": "claude-sonnet-5"
}
}
}Configuration file path:
~/.cherry-studio/config.jsonOr configure via UI in Settings -> Providers -> OpenAI:
- Custom Server Address:
http://localhost:8000/v1 - API Key:
sk-web2api-local - Models:
claude-sonnet-5,terra,sol,glm-5.2,kimi-k3
Configuration file path (Global):
~/.claude/settings.jsonConfiguration file path (Project-level):
.claude/settings.jsonAdd configuration:
{
"env": {
"OPENAI_BASE_URL": "http://localhost:8000/v1",
"OPENAI_API_KEY": "sk-web2api-local",
"ANTHROPIC_MODEL": "claude-sonnet-5"
}
}Then run:
claudeConfiguration file path:
.vscode/settings.jsonAdd configuration:
{
"cline.apiProvider": "openai-compatible",
"cline.openAiBaseUrl": "http://localhost:8000/v1",
"cline.openAiApiKey": "sk-web2api-local",
"cline.openAiModelId": "claude-sonnet-5"
}Configuration file path:
~/.codex/config.tomlAdd configuration:
[model]
provider = "openai"
base_url = "http://localhost:8000/v1"
api_key = "sk-web2api-local"
model_name = "claude-sonnet-5"Configuration file path:
~/.continue/config.jsonAdd configuration:
{
"models": [
{
"title": "Claude Sonnet 5 (Web2API)",
"provider": "openai",
"model": "claude-sonnet-5",
"apiBase": "http://localhost:8000/v1",
"apiKey": "sk-web2api-local"
},
{
"title": "GPT 5.6 Terra (Web2API)",
"provider": "openai",
"model": "terra",
"apiBase": "http://localhost:8000/v1",
"apiKey": "sk-web2api-local"
}
]
}Configuration file path (Global):
~/.cursor/User/settings.jsonConfiguration file path (Project-level):
.vscode/settings.jsonAdd configuration:
{
"cursor.openaiBaseUrl": "http://localhost:8000/v1",
"cursor.openaiApiKey": "sk-web2api-local",
"cursor.model": "claude-sonnet-5"
}Or configure in Cursor Settings -> Models:
- Toggle Override OpenAI Base URL:
http://localhost:8000/v1 - Set OpenAI API Key:
sk-web2api-local - Add model:
claude-sonnet-5
Configuration file path:
agent.yamlAdd configuration:
llm:
api_type: openai
base_url: "http://localhost:8000/v1"
api_key: "sk-web2api-local"
model: "claude-sonnet-5"
temperature: 0.7Configuration file path:
~/.hermes/config.jsonAdd configuration:
{
"llm": {
"provider": "openai",
"baseUrl": "http://localhost:8000/v1",
"apiKey": "sk-web2api-local",
"model": "claude-sonnet-5"
}
}Configuration file path:
librechat.yamlAdd configuration:
endpoints:
custom:
- name: "Web2API"
apiKey: "sk-web2api-local"
baseURL: "http://localhost:8000/v1"
models:
default: ["claude-sonnet-5", "terra", "sol", "glm-5.2", "kimi-k3"]
titleConvo: true
modelDisplayLabel: "Web2API"Configuration file path (Linux / macOS):
~/.local/share/mimocode/mimocode.jsoncConfiguration file path (Windows):
%LOCALAPPDATA%\mimocode\data\mimocode.jsoncAdd configuration:
{
"providers": {
"web2api": {
"type": "openai-compatible",
"baseUrl": "http://localhost:8000/v1",
"apiKey": "sk-web2api-local",
"models": [
"claude-sonnet-5",
"terra",
"sol",
"glm-5.2",
"kimi-k3"
]
}
},
"default_model": "web2api/claude-sonnet-5"
}Or run mimo, select Custom Provider, and set:
- Base URL:
http://localhost:8000/v1 - API Key:
sk-web2api-local - Model:
claude-sonnet-5
Configuration file path:
.env.localAdd configuration:
BASE_URL=http://localhost:8000
OPENAI_API_KEY=sk-web2api-local
CUSTOM_MODELS=-all,+claude-sonnet-5,+terra,+sol,+glm-5.2,+kimi-k3Configuration file path:
~/.omniroute/providers.jsonAdd configuration via CLI:
omniroute provider add --id web2api --type openai --base-url http://localhost:8000/v1 --api-key sk-web2api-local --models claude-sonnet-5,terra,sol,glm-5.2,kimi-k3Or add configuration to ~/.omniroute/providers.json:
{
"providers": [
{
"id": "web2api",
"name": "Web2API Gateway",
"type": "openai-compatible",
"baseUrl": "http://localhost:8000/v1",
"apiKey": "sk-web2api-local",
"models": [
"claude-sonnet-5",
"terra",
"sol",
"glm-5.2",
"kimi-k3"
]
}
]
}Configuration file path:
.envAdd environment configuration:
OPENAI_BASE_URL="http://localhost:8000/v1"
OPENAI_API_KEY="sk-web2api-local"Python SDK example:
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="sk-web2api-local"
)
response = client.chat.completions.create(
model="claude-sonnet-5",
messages=[{"role": "user", "content": "Write quicksort in Python."}],
stream=True
)
for chunk in response:
content = chunk.choices[0].delta.content or ""
print(content, end="", flush=True)Node.js SDK example:
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "http://localhost:8000/v1",
apiKey: "sk-web2api-local",
});
const response = await client.chat.completions.create({
model: "claude-sonnet-5",
messages: [{ role: "user", content: "Write quicksort in TypeScript." }],
});
console.log(response.choices[0].message.content);cURL command:
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-web2api-local" \
-d '{"model":"claude-sonnet-5","messages":[{"role":"user","content":"Hello!"}]}'Configuration file path:
openclaw.jsonAdd configuration:
{
"providers": {
"web2api": {
"type": "openai-compatible",
"baseURL": "http://localhost:8000/v1",
"apiKey": "sk-web2api-local",
"models": [
"claude-sonnet-5",
"terra",
"sol",
"glm-5.2",
"kimi-k3"
]
}
}
}Configuration file path (Linux / macOS):
~/.local/share/opencode/opencode.jsoncConfiguration file path (Project-level):
opencode.jsoncAdd configuration:
{
"providers": {
"web2api": {
"type": "openai-compatible",
"baseUrl": "http://localhost:8000/v1",
"apiKey": "sk-web2api-local",
"models": [
"claude-sonnet-5",
"terra",
"sol",
"glm-5.2",
"kimi-k3"
]
}
},
"default_model": "web2api/claude-sonnet-5"
}Configuration file path:
config.tomlAdd configuration:
[llm]
model = "openai/claude-sonnet-5"
base_url = "http://localhost:8000/v1"
api_key = "sk-web2api-local"Configuration file path:
.vscode/settings.jsonAdd configuration:
{
"roo-cline.apiProvider": "openai-compatible",
"roo-cline.openAiBaseUrl": "http://localhost:8000/v1",
"roo-cline.openAiApiKey": "sk-web2api-local",
"roo-cline.openAiModelId": "claude-sonnet-5"
}Configuration file path:
~/.trae/config.jsonAdd configuration:
{
"modelProviders": [
{
"name": "Web2API",
"apiType": "openai",
"endpoint": "http://localhost:8000/v1",
"apiKey": "sk-web2api-local",
"models": [
"claude-sonnet-5",
"terra",
"sol",
"glm-5.2",
"kimi-k3"
]
}
]
}Configuration file path:
~/.codeium/windsurf/model_config.jsonAdd configuration:
{
"customOpenAI": {
"endpoint": "http://localhost:8000/v1",
"apiKey": "sk-web2api-local",
"model": "claude-sonnet-5"
}
}Architecture & Structure
Web2API/
├── .github/
│ └── workflows/
│ └── test.yml # Automated CI test & typecheck workflow
├── src/
│ ├── types.ts # OpenAI schemas, upstream contracts, model definitions
│ ├── config.ts # Model catalog, aliases, browser headers, defaults
│ ├── services/
│ │ ├── prompt-coercer.ts # Transforms tools into strict JSON system directives
│ │ ├── json-extractor.ts # Strips markdown fences, parses JSON, formats tool_calls
│ │ ├── upstream-client.ts # HTTP client with browser headers and error parsing
│ │ └── stream-transformer.ts# Real-time SSE transformer with reasoning_content
│ ├── routes/
│ │ ├── chat.ts # /v1/chat/completions handler (streaming & tool calls)
│ │ ├── models.ts # /v1/models catalog handler
│ │ └── health.ts # /health service status endpoint
│ ├── index.ts # Universal Hono application instance (CORS & routes)
│ └── server.ts # Local Node.js server launcher (@hono/node-server)
├── worker.js # Standalone Cloudflare Workers bundle (82 KB)
├── dist/
│ └── server.js # Standalone Node.js server bundle (102 KB)
├── docs/
│ ├── Underlying_Model_Reality.md # Nemotron-3 120B reality & trace evidence
│ ├── Freemodels_Reverse_Engineering.md # Upstream architecture analysis
│ ├── Web2API_MVP_Architecture.md # Technical architecture spec
│ └── Web2API_Proxy.md # Original design guide
├── tests/ # 18 automated unit and integration tests
├── public/
│ └── Web2API_Banner.webp # Repository header banner
├── wrangler.toml # Cloudflare Workers configuration
├── package.json # Dependencies and build scripts
└── tsconfig.json # Strict TypeScript configurationAdvanced Guides
Prompt Coercion & Tool Calling
Because web frontends lack native tool_choice or function calling APIs, Web2API coerces tools into an explicit JSON contract:
Schema Injection: Injects tool parameters and format rules into the message context:
{ "name": "tool_name", "arguments": { ... } }Negative Prompting: Instructs the model to emit only valid JSON without markdown code fences (
```json), conversational greetings, or postscripts.History Normalization: Serializes previous assistant tool calls and formats tool result messages so the model maintains multi-turn reasoning context.
Resilient JSON Extractor & Sanitizer
To handle non-compliant model outputs:
- Fence Removal: Regex stripping of
json .... - Balanced-Brace Matching: Scans for outermost
{...}or[...]structures. - Repair Pipeline: Fixes unescaped newlines in diffs and strips trailing commas before
}or]. - Validation: Confirms presence of
nameandarguments, constructing standard OpenAItool_callspayloads with unique identifiers (call_...).
429 Quota Exhaustion & Rate Limits
The upstream backend aggregates API keys from Nvidia NIM and DashScope. When keys run out of quota:
Upstream returns:
{"error": "All providers exhausted. Retry in 20s..."}Web2API parses the duration, sets the
Retry-After: 20header, and wraps the error in an OpenAI standard format:{ "error": { "message": "All providers exhausted. Retry in 20s...", "type": "rate_limit_error", "code": 429 } }
Environment Variables
| Variable | Mode | Default | Description |
|---|---|---|---|
| PORT | Local | 8000 | Local HTTP daemon port |
| API_KEY | Both | (None / sk-web2api-local) | Optional client authorization bearer token. If unset, access is open. |
| UPSTREAM_URL | Both | https://freemodels-chat.freemodels.workers.dev | Upstream web chat backend endpoint |
| DEFAULT_MODEL | Both | claude-sonnet-5 | Default model when unspecified |
Development & Testing
Run TypeScript typecheck:
npm run typecheckRun automated test suite:
npm testBuild standalone bundles:
npm run buildContinuous Integration (CI)
Web2API includes a GitHub Actions pipeline (.github/workflows/test.yml) that automatically runs on every push and pull_request to main:
- Typechecking (
npm run typecheck) - Full test suite execution (
npm test) - Bundle build verification (
npm run build)
Skipping Tests on Commit
To skip automated tests on documentation or non-functional commits, include any of the following tags in your commit message:
[skip test]or[skip tests][skip ci]or[ci skip]
git commit -m "docs: update README [skip test]"Disclaimer & Legal Notice
IMPORTANT: Please read this notice carefully before using or deploying Web2API.
- Educational & Research Purposes Only: This project is developed and distributed exclusively for personal educational, research, and non-commercial API interoperability testing purposes.
- Risk of Upstream Changes: Web chat interfaces and reverse-engineered endpoints may change, become rate-limited, or terminate service at any time without warning.
- No Warranty & No Guarantee: The author and contributors make no claims, promises, or guarantees regarding the safety, status, or longevity of upstream access. This software is provided "AS IS", without warranty of any kind, express or implied.
- Assumption of Risk: You assume full and sole responsibility for any outcomes or damages resulting from using this software. Use strictly at your own risk.
- Trademark Attribution: All product names, logos, and brands (such as "Anthropic", "Claude", "OpenAI", "ChatGPT", "Xiaomi", "MiMoCode") are trademarks or registered trademarks of their respective owners. Web2API is an independent open-source project and is neither affiliated with, maintained by, nor endorsed by any of these entities.
License
This project is licensed under the GNU General Public License v3.0 (GPLv3). See the LICENSE file for details.
