Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🎨 zai-image-proxy

OpenAI & Google Gemini Compatible Image Generation API

A free, self-hosted proxy that wraps Z.AI image generation and exposes it as OpenAI and Gemini compatible API endpoints.

Node.js License: MIT Express Deploy to Render


✨ Why zai-image-proxy?

Most AI image generation APIs cost money per image. Z.AI offers high quality image generation, but with a non-standard API. This proxy bridges that gap, giving you a free, self-hosted image generation API that works with any tool or SDK that speaks OpenAI or Gemini.

Key Features

Feature Description
🔌 OpenAI Compatible Drop-in replacement for POST /v1/images/generations
💬 Chat Completions Works with POST /v1/chat/completions — compatible with Open WebUI, LibreChat, LiteLLM, etc.
🔷 Gemini Compatible Supports POST /v1/models/:model:generateContent with inlineData response
🖼️ Two Quality Tiers z-image (1K, ~30s) and z-image-hd (2K HD, ~2min)
🔄 Smart Model Aliases Send dall-e-3, gpt-image-1, gemini-2.0-flash, or imagen-3 and they all just work
🛡️ Production Hardened Rate limiting, structured logging, graceful shutdown, error boundaries, secret masking
🔑 Auto Session Refresh OAuth-based token renewal via chat.z.ai
🚀 Deploy Anywhere Render, Vercel, Railway, Docker, or any Node.js host

🚀 Quick Start

1. Clone & Install

git clone https://github.com/TheOwlKun/zai-image-proxy.git
cd zai-image-proxy
npm install

2. Configure

cp .env.example .env

Edit .env and fill in your tokens:

Z_IMAGE_SESSION=your_session_token_here
Z_CHAT_TOKEN=your_chat_token_here
API_KEY=sk-your-secret-key
PORT=3000
🔑 How to get your tokens (click to expand)

Z_IMAGE_SESSION — Required

  1. Open image.z.ai in your browser
  2. Open DevTools → F12 (or right-click → Inspect)
  3. Go to Application tab → StorageCookies → Select https://image.z.ai
  4. Find the cookie named session
  5. Copy its Value — that's your Z_IMAGE_SESSION

Z_CHAT_TOKEN — Required (for auto session refresh)

  1. Open chat.z.ai in your browser
  2. Open DevTools → F12
  3. Go to Application tab → StorageCookies → Select https://chat.z.ai
  4. Find the cookie named token
  5. Copy its Value — that's your Z_CHAT_TOKEN

💡 Tip: The Z_CHAT_TOKEN allows the proxy to automatically refresh the Z_IMAGE_SESSION when it expires, so you don't have to manually update it.

3. Run

npm start

You'll see a startup banner confirming everything is working:

╔══════════════════════════════════════════════════════════════╗
║              zai-image-proxy v1.0.0                         ║
║          OpenAI + Gemini Compatible Image API               ║
╠══════════════════════════════════════════════════════════════╣
║  Port:      3000                                            ║
║  Session:   ✓ Valid (29 days left)                           ║
║  API Key:   ✓ Configured                                    ║
╚══════════════════════════════════════════════════════════════╝

📡 API Reference

🟢 OpenAI Images API

POST /v1/images/generations

The standard OpenAI image generation endpoint. Compatible with the official OpenAI SDK.

curl -X POST http://localhost:3000/v1/images/generations \
  -H "Authorization: Bearer sk-your-secret-key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "a serene mountain landscape at sunset, digital art",
    "model": "z-image-hd",
    "size": "1024x1024",
    "quality": "hd",
    "response_format": "b64_json",
    "n": 1
  }'

Parameters:

Parameter Type Default Description
prompt string required Text description of the image
model string z-image Model to use (see Model Mapping)
n integer 1 Number of images (1–4, generated sequentially)
size string 1024x1024 Image dimensions (see Supported Sizes)
quality string standard standard (1K) or hd (2K)
response_format string b64_json b64_json or url
style string Accepted for compatibility but has no effect

💬 OpenAI Chat Completions

POST /v1/chat/completions

Send a chat message and receive a generated image — makes the proxy work with tools like Open WebUI, LibreChat, LiteLLM, Cursor, and any OpenAI SDK.

curl -X POST http://localhost:3000/v1/chat/completions \
  -H "Authorization: Bearer sk-your-secret-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "dall-e-3",
    "messages": [
      { "role": "user", "content": "Generate a cyberpunk city at night with neon lights" }
    ]
  }'

The response wraps the generated image in standard multimodal chat completion format with image_url content parts.


🔷 Google Gemini API

POST /v1/models/:model:generateContent

Compatible with Google's GenAI SDK. Supports x-goog-api-key header and ?key= query parameter.

curl -X POST "http://localhost:3000/v1/models/gemini-2.0-flash:generateContent" \
  -H "x-goog-api-key: sk-your-secret-key" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [{
      "parts": [{ "text": "A futuristic cityscape with flying cars" }]
    }],
    "generationConfig": {
      "responseModalities": ["TEXT", "IMAGE"]
    }
  }'

Returns image as inlineData with mimeType and base64 data in the standard Gemini candidates response.


⚡ Native Z.AI API

POST /generate — Direct access to Z.AI parameters.

curl -X POST http://localhost:3000/generate \
  -H "Authorization: Bearer sk-your-secret-key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "a cute cat with blue eyes",
    "ratio": "16:9",
    "resolution": "2K",
    "noWatermark": true
  }'

GET /images — List your past generations (paginated).

GET /options — List all supported ratios, resolutions, and size mappings.


📋 Other Endpoints

Method Path Auth Description
GET /v1/models Yes List all available models
GET /v1/models/:id Yes Get a specific model
GET /health No Health check (for platform monitoring)
GET /session Yes View session status
POST /session Yes Set session or chat token via API
POST /session/refresh Yes Force session token refresh

🐍 SDK Examples

Python — OpenAI SDK
from openai import OpenAI

client = OpenAI(
    api_key="sk-your-secret-key",
    base_url="http://localhost:3000/v1"
)

response = client.images.generate(
    model="z-image-hd",
    prompt="a serene mountain landscape at sunset",
    size="1024x1024",
    response_format="b64_json",
    n=1
)

import base64
image_data = base64.b64decode(response.data[0].b64_json)
with open("generated.png", "wb") as f:
    f.write(image_data)
print("✓ Image saved to generated.png")
Node.js — OpenAI SDK
import OpenAI from 'openai';
import fs from 'fs';

const client = new OpenAI({
    apiKey: 'sk-your-secret-key',
    baseURL: 'http://localhost:3000/v1',
});

const response = await client.images.generate({
    model: 'z-image-hd',
    prompt: 'a serene mountain landscape at sunset',
    size: '1024x1024',
    response_format: 'b64_json',
    n: 1,
});

const imageBuffer = Buffer.from(response.data[0].b64_json, 'base64');
fs.writeFileSync('generated.png', imageBuffer);
console.log('✓ Image saved to generated.png');
Python — Chat Completions
from openai import OpenAI

client = OpenAI(
    api_key="sk-your-secret-key",
    base_url="http://localhost:3000/v1"
)

response = client.chat.completions.create(
    model="dall-e-3",
    messages=[
        {"role": "user", "content": "Generate an image of a magical forest"}
    ]
)

# Image is embedded as base64 in the response content
print(response.choices[0].message.content)

🗺️ Model Mapping

Any client SDK can use familiar model names — they're automatically mapped to Z.AI's engine.

Client sends Resolves to Resolution Speed
z-image z-image 1K ~30s
z-image-hd z-image-hd 2K (HD) ~2min
dall-e-2 z-image 1K ~30s
dall-e-3 z-image-hd 2K (HD) ~2min
gpt-image-1 z-image-hd 2K (HD) ~2min
gpt-image-2 z-image-hd 2K (HD) ~2min
gemini-2.0-flash z-image-hd 2K (HD) ~2min
imagen-3 z-image-hd 2K (HD) ~2min

Note: Any unrecognized model name defaults to z-image (1K).


📐 Supported Sizes & Ratios

OpenAI size Z.AI Ratio Orientation
1024x1024 1:1 Square
1792x1024 16:9 Landscape
1024x1792 9:16 Portrait
1920x1080 16:9 Landscape
1080x1920 9:16 Portrait
1280x720 16:9 Landscape
720x1280 9:16 Portrait

Additional ratios via the native /generate endpoint: 3:4, 4:3, 21:9, 9:21.


⚙️ Environment Variables

Variable Required Default Description
Z_IMAGE_SESSION Yes Session token from image.z.ai cookies
Z_CHAT_TOKEN Yes Chat token from chat.z.ai for auto-refresh
API_KEY Yes API key for all authenticated endpoints
PORT Yes 3000 Server port
Z_USER_AGENT No random Fixed UA string (if not set, rotates randomly per request)
RATE_LIMIT_MAX No 30 Max requests per window per IP
RATE_LIMIT_WINDOW_MS No 60000 Rate limit window duration (ms)
GENERATION_TIMEOUT No 180000 Image generation timeout (ms)
DOWNLOAD_TIMEOUT No 30000 Image download timeout (ms)
LOG_LEVEL No info debug · info · warn · error
CORS_ORIGINS No * Comma-separated allowed origins
NODE_ENV No development development or production

See .env.example for the complete template.


🚢 Deployment

Render (Recommended)

Deploy to Render

Render runs a real server with no timeout limits, perfect for HD generation.

  1. Fork this repo
  2. Click the button above or go to render.com > New > Web Service
  3. Connect your forked repo
  4. Set environment variables: Z_IMAGE_SESSION, Z_CHAT_TOKEN, API_KEY
  5. Deploy!

Or use the included Render Blueprint for one-click setup.

Vercel

Deploy with Vercel

npm i -g vercel
vercel

⚠️ Vercel Limitation: Free tier has a 60 second function timeout. 2K (HD) generation can take around 2 minutes, so use z-image (1K) for reliability on Vercel.

Railway

Deploy on Railway

  1. Fork this repo
  2. Click the button above or go to railway.com > New Project > Deploy from GitHub
  3. Connect your fork, set environment variables
  4. Done!

Docker

docker build -t zai-image-proxy .
docker run -p 3000:3000 --env-file .env zai-image-proxy

The Dockerfile uses a multi-stage Alpine build (~50MB), runs as non-root, and includes a health check.


🏥 Health Check

curl http://localhost:3000/health

Returns session validity, expiry info, and server status. No authentication required — use this for platform monitoring and uptime checks.


🛡️ Production Features

Feature Details
Rate Limiting In-memory sliding window per IP (configurable)
Request Timeouts Prevents hung connections
Graceful Shutdown SIGTERM/SIGINT with 10s connection draining
Input Validation All parameters validated before hitting Z.AI
Error Boundaries Global handler + unhandledRejection catch
Structured Logging Request IDs, timestamps, levels, masked secrets
Secret Masking Tokens and API keys are never logged in full
Auto Session Refresh Proactive refresh when token nears expiry
CORS Configurable via CORS_ORIGINS env
Serverless Safe No filesystem dependency by default
Fail-Fast Startup Missing config = clear warning at boot

⚠️ Limitations

This proxy wraps Z.AI's image generation service. The following features are not supported because Z.AI doesn't offer them:

Feature Status
Image editing / inpainting ❌ Not supported
Image variations ❌ Not supported
Streaming responses ❌ Not supported
Negative prompts ❌ Not supported
revised_prompt field ❌ Omitted (not faked)
Batch generation (n > 1) ⚠️ Runs sequentially
style parameter ⚠️ Accepted but ignored

📁 Project Structure

zai-image-proxy/
├── api/
│   └── index.js              ← Vercel serverless entry
├── src/
│   ├── app.js                ← Express app factory
│   ├── config.js             ← Centralized configuration
│   ├── errors.js             ← Custom error hierarchy
│   ├── logger.js             ← Structured logger + secret masking
│   ├── models.js             ← Model definitions & alias mapping
│   ├── z-image-client.js     ← Core Z.AI reverse-engineered client
│   ├── middleware/
│   │   ├── auth.js           ← Bearer + Gemini API key auth
│   │   ├── rate-limiter.js   ← In-memory sliding window
│   │   └── error-handler.js  ← Global error handler
│   └── routes/
│       ├── openai-images.js  ← POST /v1/images/generations
│       ├── openai-chat.js    ← POST /v1/chat/completions
│       ├── openai-models.js  ← GET /v1/models
│       ├── gemini.js         ← POST /v1/models/:model:generateContent
│       ├── native.js         ← POST /generate, GET /images
│       └── health.js         ← GET /health, session management
├── index.js                  ← Standalone server entry point
├── vercel.json               ← Vercel routing config
├── render.yaml               ← Render Blueprint
├── Dockerfile                ← Multi-stage Alpine build
└── package.json              ← 4 dependencies only

🤝 Contributing

Contributions are welcome! Here's how:

  1. Fork the repo
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Commit your changes: git commit -m 'Add amazing feature'
  4. Push to the branch: git push origin feature/amazing-feature
  5. Open a Pull Request

📄 License

This project is licensed under the MIT License.


Made with ❤️ by TheOwlKun

⭐ Star this repo if you find it useful!

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages