- π¨ Text-to-image / image-to-image
- π¬ Text-to-video / image-to-video
- ποΈ First-and-last-frame video generation
- π Automatic AT/ST refresh - Refreshes expired access tokens automatically and renews expired session tokens through the browser in
personalmode - π Credit display - Queries and displays VideoFX credits in real time
- π Load balancing - Multi-token rotation and concurrency control
- π Proxy support - Supports HTTP and SOCKS5 proxies
- π± Web administration interface - Intuitive token and configuration management
- π¨ Continuous image-generation conversations
- π§© Official Gemini request compatibility - Supports
generateContent,streamGenerateContent,systemInstruction, andcontents.parts.text/inlineData/fileData - β
Verified official Gemini image output - Tested with a real token to confirm that
/models/{model}:generateContentreturnscandidates[].content.parts[].inlineData
- uv and Bun for the shortest local setup. uv installs Python 3.11 when necessary.
- Docker and Docker Compose only when you want container deployment.
apps/api/: the installableflow2apiPython package and backend testsapps/admin-web/: the React administration interfaceapps/captcha-extension/: the Chrome CAPTCHA/account-sync workerapps/metadata-extension/: the TypeScript metadata extensionapps/agent-gateway/: the standalone remote browser agent gatewayinfra/docker/andinfra/compose/: container images and Compose stackspackages/api-contract/: TypeScript contracts generated from the backend OpenAPI snapshotpackages/extension-core/: stable browser-independent extension transport/storage primitives.runtime/: local databases, browser profiles, generated files, and cache data
If you are upgrading a checkout created before this layout, stop Flow2API and move the old local runtime directories once:
mkdir -p .runtime
mv data .runtime/data
mv tmp .runtime/tmpSkip a move when its legacy directory does not exist. Do not overwrite an
existing .runtime/data; back up both directories and reconcile them first.
Flow now requires an additional CAPTCHA. You can solve it through a browser or a third-party service.
-
To use YesCaptcha, register here, obtain an API key, and enter it in the YesCaptcha API key field on the system settings page.
-
The admin UI supports these YesCaptcha task types:
RecaptchaV3TaskProxyless,RecaptchaV3TaskProxylessM1,RecaptchaV3TaskProxylessM1S7, andRecaptchaV3TaskProxylessM1S9.M1S9is currently recommended by default. S7 and S9 forceminScorevalues of 0.7 and 0.9 respectively. -
The default
infra/compose/docker-compose.ymlis intended for third-party solvers such as YesCaptcha, CapMonster, EzCaptcha, or CapSolver. For headedbrowserorpersonalsolving inside Docker, useinfra/compose/docker-compose.headed.ymlbelow. -
To test
remote_browsermode locally, run the Node mock solver on the host. It verifies HTTP and authentication only and does not produce real reCAPTCHA tokens. Seetools/remote-browser-mock/. -
For the production Agent Gateway (Flow2API over HTTP, with jobs delivered to user PCs over WebSocket), see
apps/agent-gateway/src/flow2api_gateway/README.md. -
For asynchronous submission and polling through
/v1/async/chat/completionsand/v1/jobs/{job_id}, seedocs/async-polling.md. -
Runway web-task integration is available through the admin
Runwaytab,runway-*models, and/v1/runway/*routes. Seedocs/runway.md. It includes a manifest-backed model registry, live feature sync, real Runway uploads/datasets, image/video/audio/upscale task builders, OpenAI-compatible dispatch, voices, estimates, cancel, async polling, and cache mirroring. -
Production performance, Railway Redis, WebSocket events, and seven-day retention are documented in
docs/performance-redis-rollout.md. The PostgreSQL 16 bridge, migration, encrypted Google Drive backup, cutover, and rollback procedure is indocs/postgres-migration-runbook.md. -
The bundled
apps/captcha-extension/supports CAPTCHA work, current-account import, scheduled ST/cookie synchronization, and token-bound refresh workers. Build it withbun run --cwd apps/captcha-extension build, then loadapps/captcha-extension/dist/in Chrome.
When using captcha method extension, Flow2API keeps one global captcha mode but isolates end-user workers per managed API key.
- Run
bun install --frozen-lockfileandbun run --cwd apps/captcha-extension build, then loadapps/captcha-extension/dist/as an unpacked Chrome extension. - Create a managed API key in API key manager. Add
tokens:importif this extension may add or refresh the Google account signed in to its Chrome profile. - Select End User Worker, enter the WebSocket URL and managed API key, then save the connection.
- Click Save / Import Current Google Account to create or update the dashboard token by Google email. The imported account is automatically assigned to that managed key, uses
protocolrefresh, and stores the required Google cookies. - Optionally enable automatic account sync and choose its interval. This keeps the account current without a separate Refresh Worker profile.
Use Captcha Worker for server-wide CAPTCHA capacity, or Refresh Worker only when binding a dedicated Chrome profile to an already existing Token ID.
If a managed key has no matching extension worker online, requests wait up to extension_queue_wait_timeout_seconds and then fail (no gateway fallback).
# Clone the project
git clone https://github.com/agmmnn/flow2api.git
cd flow2api
# Start the service
docker compose -f infra/compose/docker-compose.yml up -d
# Follow the logs
docker compose -f infra/compose/docker-compose.yml logs -fCompose mounts
.runtime/tmpby default. Setting the cache timeout to0means files do not expire automatically. Keep this mount if cached files must survive container recreation.
# Start with the WARP proxy
docker compose -f infra/compose/docker-compose.proxy.yml up -d
# Follow the logs
docker compose -f infra/compose/docker-compose.proxy.yml logs -fUse this mode when you need a virtual desktop and headed browser-based CAPTCHA solving inside the container. It starts
Xvfb + Fluxboxfor an internal visual desktop and setsALLOW_DOCKER_HEADED_CAPTCHA=true. Only the application port is exposed; no remote desktop port is provided. The built-inpersonalbrowser now starts headed by default. SetPERSONAL_BROWSER_HEADLESS=trueto temporarily switch it back to headless mode.
# Start headed mode (use --build on the first run)
docker compose -f infra/compose/docker-compose.headed.yml up -d --build
# Follow the logs
docker compose -f infra/compose/docker-compose.headed.yml logs -f- API port:
8000 - In the admin interface, set the CAPTCHA method to
browserorpersonal
Run cloudflared in Docker and expose the OpenAI-compatible API (/v1/...) and web administration interface (/ and /api/...) through two public subdomains backed by the same internal service. Both hostnames proxy to http://<service-name>:8000 inside Docker; the application remains a single process.
- In Cloudflare Zero Trust, open Networks β Tunnels, create a named tunnel, and copy the TUNNEL_TOKEN from the
cloudflaredinstallation command. - Configure two Public hostnames on the same tunnel (replace these examples with your own domains):
- API only (no admin UI or frontend):
https://flow-api.prismacreative.onlineβhttp://flow2api:8000 - Admin UI and frontend:
https://admin-flow.prismacreative.onlineβhttp://flow2api:8000Docker resolvesflow2apito the application container on the shared network. Do not use a host-mapped port such as38000as the tunnel origin.
- API only (no admin UI or frontend):
- Run
cp .env.example .envin the repository root and setTUNNEL_TOKEN=....infra/compose/docker-compose.ymlcontains the main flow2api service. Merginginfra/compose/docker-compose.agent.ymladds agent-gateway, redis, and cloudflared on the same tunnel. Configureagents.*in Cloudflare to usehttp://agent-gateway:9080.FLOW2API_API_ONLY_HOSTis defined on theflow2apiservice. On that hostname,ApiOnlyHostMiddlewareexposes only OpenAI-compatible routes (/v1/...), Gemini-style routes (/v1beta/models/...:generateContent,:streamGenerateContent,/models/...), cache files under/tmp,/openapi.json, and/health. It blocks the web UI,/apiadministration routes, and/assets. Always use a different hostname such asadmin-flow.*for the admin UI. Override it withFLOW2API_API_ONLY_HOST=your-api-subdomain. Do not run anothercloudflaredconnector with the same tunnel token on the host. - Start the merged stack with
docker compose -f infra/compose/docker-compose.yml -f infra/compose/docker-compose.agent.yml up -d. Add--buildto build agent-gateway. To run only the local application without the tunnel or agent, usedocker compose -f infra/compose/docker-compose.yml up -d. To build the main image from source, rundocker build -t flow2api:local -f infra/docker/Dockerfile ., set theflow2apiimage in Compose toflow2api:local, and runup. - Open the admin-flow hostname for administration and use the flow-api hostname as the OpenAI-compatible API base URL, for example
https://flow-api.prismacreative.online/v1/.... See the Agent Gateway runbook for the public Agent Gateway. - Set
[cache].base_urlinconfig/setting.tomlto the public API URL, for examplebase_url = "https://flow-api.prismacreative.online". See the comments inconfig/setting_example.toml. - Configure
FLOW2API_API_ONLY_HOSTas an environment variable. The default is shown in theflow2apiservice ininfra/compose/docker-compose.yml; Docker Compose reads the root.envfile.
If /login or another UI page remains accessible on the flow-api hostname: the current image does not contain this repository's ApiOnlyHostMiddleware, usually because it is an older ghcr.io/.../flow2api:latest image. Build and deploy from this repository with docker build -t flow2api:local -f infra/docker/Dockerfile ., set the Compose service image to flow2api:local, and run up -d again. Confirm that the startup log contains API-only host(s). The environment variable can also be set when running uv run flow2api directly. If the current image is deployed but the old page remains, disable aggressive HTML caching for that hostname or purge the Cloudflare cache.
For headed CAPTCHA solving, use infra/compose/docker-compose.headed.yml, which already includes Cloudflare Tunnel and flow2api-headed:
docker compose -f infra/compose/docker-compose.headed.yml up -dIn Zero Trust, set both public hostnames' origin to http://flow2api-headed:8000, matching the service name in infra/compose/docker-compose.headed.yml.
# Clone the project
git clone https://github.com/agmmnn/flow2api.git
cd flow2api
# Create/sync the environment and build the frontend
uv run setup
# Start Flow2API
uv run flow2apiuv run setup installs Python 3.11 if needed, creates .venv, installs the exact versions from uv.lock, installs the locked workspace dependencies with Bun, and builds the admin UI into apps/api/static/. Run it after cloning or when frontend dependencies change.
uv run flow2api starts the backend without rebuilding the frontend. For development, update Python dependencies with uv add/uv remove, then commit both pyproject.toml and uv.lock.
For upgrades, database adoption, compatibility guarantees, verification evidence,
and rollback boundaries, see docs/architecture-migration-release.md.
After startup, open the administration interface at http://localhost:8000. Change the default password immediately after your first login.
- Username:
admin - Password:
admin
GET /health: Public health check with service status and summaries for active, expiring, expired, and rate-limited tokensGET /metrics: Prometheus metricsGET /api/tokens: Admin endpoint with token state such asat_expires,at_expired,at_expiring_within_1h,ban_reason, andconsecutive_error_count
Prometheus can scrape /metrics directly. For Kubernetes deployments, scrape it only inside the cluster and restrict external access at the Ingress or Gateway layer.
Open http://localhost:8000/test to use the built-in model test page. It supports:
- Browsing available models by category, including image generation, text/image-to-video, reference-image video, and video upscaling
- One-click prompt testing with streamed generation progress
- Image uploads for image-to-image and image-to-video requests
- Direct image or video previews after generation
| Model | Description | Orientation |
|---|---|---|
gemini-2.5-flash-image-landscape |
Text/image-to-image | Landscape |
gemini-2.5-flash-image-portrait |
Text/image-to-image | Portrait |
gemini-3.0-pro-image-landscape |
Text/image-to-image | Landscape |
gemini-3.0-pro-image-portrait |
Text/image-to-image | Portrait |
gemini-3.0-pro-image-square |
Text/image-to-image | Square |
gemini-3.0-pro-image-four-three |
Text/image-to-image | Landscape 4:3 |
gemini-3.0-pro-image-three-four |
Text/image-to-image | Portrait 3:4 |
gemini-3.0-pro-image-landscape-2k |
Text/image-to-image (2K) | Landscape |
gemini-3.0-pro-image-portrait-2k |
Text/image-to-image (2K) | Portrait |
gemini-3.0-pro-image-square-2k |
Text/image-to-image (2K) | Square |
gemini-3.0-pro-image-four-three-2k |
Text/image-to-image (2K) | Landscape 4:3 |
gemini-3.0-pro-image-three-four-2k |
Text/image-to-image (2K) | Portrait 3:4 |
gemini-3.0-pro-image-landscape-4k |
Text/image-to-image (4K) | Landscape |
gemini-3.0-pro-image-portrait-4k |
Text/image-to-image (4K) | Portrait |
gemini-3.0-pro-image-square-4k |
Text/image-to-image (4K) | Square |
gemini-3.0-pro-image-four-three-4k |
Text/image-to-image (4K) | Landscape 4:3 |
gemini-3.0-pro-image-three-four-4k |
Text/image-to-image (4K) | Portrait 3:4 |
imagen-4.0-generate-preview-landscape |
Text/image-to-image | Landscape |
imagen-4.0-generate-preview-portrait |
Text/image-to-image | Portrait |
gemini-3.1-flash-image-landscape |
Text/image-to-image | Landscape |
gemini-3.1-flash-image-portrait |
Text/image-to-image | Portrait |
gemini-3.1-flash-image-square |
Text/image-to-image | Square |
gemini-3.1-flash-image-four-three |
Text/image-to-image | Landscape 4:3 |
gemini-3.1-flash-image-three-four |
Text/image-to-image | Portrait 3:4 |
gemini-3.1-flash-image-landscape-2k |
Text/image-to-image (2K) | Landscape |
gemini-3.1-flash-image-portrait-2k |
Text/image-to-image (2K) | Portrait |
gemini-3.1-flash-image-square-2k |
Text/image-to-image (2K) | Square |
gemini-3.1-flash-image-four-three-2k |
Text/image-to-image (2K) | Landscape 4:3 |
gemini-3.1-flash-image-three-four-2k |
Text/image-to-image (2K) | Portrait 3:4 |
gemini-3.1-flash-image-landscape-4k |
Text/image-to-image (4K) | Landscape |
gemini-3.1-flash-image-portrait-4k |
Text/image-to-image (4K) | Portrait |
gemini-3.1-flash-image-square-4k |
Text/image-to-image (4K) | Square |
gemini-3.1-flash-image-four-three-4k |
Text/image-to-image (4K) | Landscape 4:3 |
gemini-3.1-flash-image-three-four-4k |
Text/image-to-image (4K) | Portrait 3:4 |
| Model | Description | Orientation |
|---|---|---|
veo_3_1_t2v_fast_portrait |
Text-to-video | Portrait |
veo_3_1_t2v_fast_landscape |
Text-to-video | Landscape |
veo_3_1_t2v_fast_portrait_ultra |
Text-to-video | Portrait |
veo_3_1_t2v_fast_ultra |
Text-to-video | Landscape |
veo_3_1_t2v_fast_portrait_ultra_relaxed |
Text-to-video | Portrait |
veo_3_1_t2v_fast_ultra_relaxed |
Text-to-video | Landscape |
veo_3_1_t2v_portrait |
Text-to-video | Portrait |
veo_3_1_t2v_landscape |
Text-to-video | Landscape |
veo_3_1_t2v_lite_portrait |
Text-to-video Lite | Portrait |
veo_3_1_t2v_lite_landscape |
Text-to-video Lite | Landscape |
veo_3_1_t2v_landscape_4s |
Text-to-video, 4 seconds | Landscape |
veo_3_1_t2v_portrait_4s |
Text-to-video, 4 seconds | Portrait |
veo_3_1_t2v_landscape_6s |
Text-to-video, 6 seconds | Landscape |
veo_3_1_t2v_portrait_6s |
Text-to-video, 6 seconds | Portrait |
veo_3_1_t2v_fast_landscape_4s |
Fast text-to-video, 4 seconds | Landscape |
veo_3_1_t2v_fast_portrait_4s |
Fast text-to-video, 4 seconds | Portrait |
veo_3_1_t2v_fast_landscape_6s |
Fast text-to-video, 6 seconds | Landscape |
veo_3_1_t2v_fast_portrait_6s |
Fast text-to-video, 6 seconds | Portrait |
veo_3_1_t2v_lite_4s_portrait |
Text-to-video Lite, 4 seconds | Portrait |
veo_3_1_t2v_lite_4s_landscape |
Text-to-video Lite, 4 seconds | Landscape |
veo_3_1_t2v_lite_6s_portrait |
Text-to-video Lite, 6 seconds | Portrait |
veo_3_1_t2v_lite_6s_landscape |
Text-to-video Lite, 6 seconds | Landscape |
πΈ Supports one or two images: one image is the first frame; two images are the first and last frames
π‘ Automatic selection: the system chooses the appropriate
model_keyfrom the image count.
- Single-frame mode (one image): generates a video from the first frame
- Two-frame mode (two images): generates a transition between the first and last frames
veo_3_1_i2v_lite_*supports only one first-frame imageveo_3_1_interpolation_lite_*supports exactly two first/last-frame images
| Model | Description | Orientation |
|---|---|---|
veo_3_1_i2v_s_fast_portrait_fl |
Image-to-video | Portrait |
veo_3_1_i2v_s_fast_fl |
Image-to-video | Landscape |
veo_3_1_i2v_s_fast_portrait_ultra_fl |
Image-to-video | Portrait |
veo_3_1_i2v_s_fast_ultra_fl |
Image-to-video | Landscape |
veo_3_1_i2v_s_fast_portrait_ultra_relaxed |
Image-to-video | Portrait |
veo_3_1_i2v_s_fast_ultra_relaxed |
Image-to-video | Landscape |
veo_3_1_i2v_s_portrait |
Image-to-video | Portrait |
veo_3_1_i2v_s_landscape |
Image-to-video | Landscape |
veo_3_1_i2v_lite_portrait |
Image-to-video Lite (first frame only) | Portrait |
veo_3_1_i2v_lite_landscape |
Image-to-video Lite (first frame only) | Landscape |
veo_3_1_interpolation_lite_portrait |
Image-to-video Lite (first/last-frame transition) | Portrait |
veo_3_1_interpolation_lite_landscape |
Image-to-video Lite (first/last-frame transition) | Landscape |
veo_3_1_i2v_s_landscape_4s |
Image-to-video, 4 seconds | Landscape |
veo_3_1_i2v_s_portrait_4s |
Image-to-video, 4 seconds | Portrait |
veo_3_1_i2v_s_landscape_6s |
Image-to-video, 6 seconds | Landscape |
veo_3_1_i2v_s_portrait_6s |
Image-to-video, 6 seconds | Portrait |
veo_3_1_i2v_s_fast_landscape_4s_fl |
Fast image-to-video, 4 seconds | Landscape |
veo_3_1_i2v_s_fast_portrait_4s_fl |
Fast image-to-video, 4 seconds | Portrait |
veo_3_1_i2v_s_fast_landscape_6s_fl |
Fast image-to-video, 6 seconds | Landscape |
veo_3_1_i2v_s_fast_portrait_6s_fl |
Fast image-to-video, 6 seconds | Portrait |
veo_3_1_i2v_lite_4s_portrait |
Image-to-video Lite, 4 seconds (first frame only) | Portrait |
veo_3_1_i2v_lite_4s_landscape |
Image-to-video Lite, 4 seconds (first frame only) | Landscape |
veo_3_1_i2v_lite_6s_portrait |
Image-to-video Lite, 6 seconds (first frame only) | Portrait |
veo_3_1_i2v_lite_6s_landscape |
Image-to-video Lite, 6 seconds (first frame only) | Landscape |
veo_3_1_interpolation_lite_4s_portrait |
Image-to-video Lite, 4 seconds (first/last-frame transition) | Portrait |
veo_3_1_interpolation_lite_4s_landscape |
Image-to-video Lite, 4 seconds (first/last-frame transition) | Landscape |
veo_3_1_interpolation_lite_6s_portrait |
Image-to-video Lite, 6 seconds (first/last-frame transition) | Portrait |
veo_3_1_interpolation_lite_6s_landscape |
Image-to-video Lite, 6 seconds (first/last-frame transition) | Landscape |
πΌοΈ Supports multiple reference images
2026-03-06 update
- Synchronized with the latest upstream
R2Vvideo request body- Replaced
textInputwithstructuredPrompt.parts- Added top-level
mediaGenerationContext.batchId- Added top-level
useV2ModelConfig: true- Landscape and portrait
R2Vmodels now share the same request body- The upstream landscape
videoModelKeynow uses the*_landscapeform- The current upstream protocol accepts at most three
referenceImages
| Model | Description | Orientation |
|---|---|---|
veo_3_1_r2v_fast_portrait |
Reference-image-to-video | Portrait |
veo_3_1_r2v_fast_landscape |
Reference-image-to-video | Landscape |
veo_3_1_r2v_fast_portrait_ultra |
Reference-image-to-video | Portrait |
veo_3_1_r2v_fast_landscape_ultra |
Reference-image-to-video | Landscape |
veo_3_1_r2v_fast_portrait_ultra_relaxed |
Reference-image-to-video | Portrait |
veo_3_1_r2v_fast_landscape_ultra_relaxed |
Reference-image-to-video | Landscape |
These models first generate a video with the corresponding standard Veo 3.1 model and then submit a 1080p or 4K upscale request. They do not call an upstream upscaler model key directly.
| Model | Description | Output |
|---|---|---|
veo_3_1_t2v_landscape_4k |
Text-to-video upscale | 4K |
veo_3_1_t2v_portrait_4k |
Text-to-video upscale | 4K |
veo_3_1_t2v_landscape_1080p |
Text-to-video upscale | 1080p |
veo_3_1_t2v_portrait_1080p |
Text-to-video upscale | 1080p |
veo_3_1_t2v_landscape_4s_4k |
4-second text-to-video upscale | 4K |
veo_3_1_t2v_portrait_4s_4k |
4-second text-to-video upscale | 4K |
veo_3_1_t2v_landscape_4s_1080p |
4-second text-to-video upscale | 1080p |
veo_3_1_t2v_portrait_4s_1080p |
4-second text-to-video upscale | 1080p |
veo_3_1_t2v_landscape_6s_4k |
6-second text-to-video upscale | 4K |
veo_3_1_t2v_portrait_6s_4k |
6-second text-to-video upscale | 4K |
veo_3_1_t2v_landscape_6s_1080p |
6-second text-to-video upscale | 1080p |
veo_3_1_t2v_portrait_6s_1080p |
6-second text-to-video upscale | 1080p |
veo_3_1_t2v_fast_portrait_4k |
Text-to-video upscale | 4K |
veo_3_1_t2v_fast_4k |
Text-to-video upscale | 4K |
veo_3_1_t2v_fast_portrait_ultra_4k |
Text-to-video upscale | 4K |
veo_3_1_t2v_fast_ultra_4k |
Text-to-video upscale | 4K |
veo_3_1_t2v_fast_portrait_1080p |
Text-to-video upscale | 1080p |
veo_3_1_t2v_fast_1080p |
Text-to-video upscale | 1080p |
veo_3_1_t2v_fast_portrait_ultra_1080p |
Text-to-video upscale | 1080p |
veo_3_1_t2v_fast_ultra_1080p |
Text-to-video upscale | 1080p |
veo_3_1_i2v_s_fast_portrait_ultra_fl_4k |
Image-to-video upscale | 4K |
veo_3_1_i2v_s_fast_ultra_fl_4k |
Image-to-video upscale | 4K |
veo_3_1_i2v_s_fast_portrait_ultra_fl_1080p |
Image-to-video upscale | 1080p |
veo_3_1_i2v_s_fast_ultra_fl_1080p |
Image-to-video upscale | 1080p |
veo_3_1_i2v_s_landscape_4k |
Image-to-video upscale | 4K |
veo_3_1_i2v_s_portrait_4k |
Image-to-video upscale | 4K |
veo_3_1_i2v_s_landscape_1080p |
Image-to-video upscale | 1080p |
veo_3_1_i2v_s_portrait_1080p |
Image-to-video upscale | 1080p |
veo_3_1_i2v_s_landscape_4s_4k |
4-second image-to-video upscale | 4K |
veo_3_1_i2v_s_portrait_4s_4k |
4-second image-to-video upscale | 4K |
veo_3_1_i2v_s_landscape_4s_1080p |
4-second image-to-video upscale | 1080p |
veo_3_1_i2v_s_portrait_4s_1080p |
4-second image-to-video upscale | 1080p |
veo_3_1_i2v_s_landscape_6s_4k |
6-second image-to-video upscale | 4K |
veo_3_1_i2v_s_portrait_6s_4k |
6-second image-to-video upscale | 4K |
veo_3_1_i2v_s_landscape_6s_1080p |
6-second image-to-video upscale | 1080p |
veo_3_1_i2v_s_portrait_6s_1080p |
6-second image-to-video upscale | 1080p |
veo_3_1_r2v_fast_portrait_ultra_4k |
Reference-image video upscale | 4K |
veo_3_1_r2v_fast_landscape_ultra_4k |
Reference-image video upscale | 4K |
veo_3_1_r2v_fast_portrait_ultra_1080p |
Reference-image video upscale | 1080p |
veo_3_1_r2v_fast_landscape_ultra_1080p |
Reference-image video upscale | 1080p |
In addition to the OpenAI-compatible examples below, the service supports the official Gemini format:
POST /v1beta/models/{model}:generateContentPOST /models/{model}:generateContentPOST /v1beta/models/{model}:streamGenerateContentPOST /models/{model}:streamGenerateContentOfficial Gemini requests support these authentication methods:
Authorization: Bearer <api_key>x-goog-api-key: <api_key>?key=<api_key>Official Gemini image requests support:
systemInstructioncontents[].parts[].textcontents[].parts[].inlineDatacontents[].parts[].fileData.fileUrigenerationConfig.responseModalitiesgenerationConfig.imageConfig.aspectRatiogenerationConfig.imageConfig.imageSize
Verified with a real token. For streaming output, replace the path suffix with
:streamGenerateContent?alt=sse.
curl -X POST "http://localhost:8000/models/gemini-3.1-flash-image:generateContent" \
-H "x-goog-api-key: han1234" \
-H "Content-Type: application/json" \
-d '{
"systemInstruction": {
"parts": [
{
"text": "Return an image only."
}
]
},
"contents": [
{
"role": "user",
"parts": [
{
"text": "A red apple on a wooden table, studio lighting, minimalist background"
}
]
}
],
"generationConfig": {
"responseModalities": ["IMAGE"],
"imageConfig": {
"aspectRatio": "1:1",
"imageSize": "1K"
}
}
}'curl -X POST "http://localhost:8000/v1/chat/completions" \
-H "Authorization: Bearer han1234" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3.1-flash-image-landscape",
"messages": [
{
"role": "user",
"content": "A cute cat playing in a garden"
}
],
"stream": true
}'Native Flow image and video requests use automatic project rotation by default. To keep generated assets in a specific tracked project, give the managed API key the projects:read scope, list its available projects, and pass project_id with the generation request:
curl "http://localhost:8000/v1/projects?limit=100" \
-H "Authorization: Bearer <managed-api-key>"
curl -X POST "http://localhost:8000/v1/chat/completions" \
-H "Authorization: Bearer <managed-api-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3.1-flash-image-landscape",
"project_id": "<flow-project-id>",
"messages": [{"role": "user", "content": "A cute cat playing in a garden"}],
"stream": true
}'The project must be active, belong to that API key, and use an account assigned to the key. Omit project_id to retain automatic routing. Project pinning applies only to native Flow models, not Runway or GeminiGen providers.
curl -X POST "http://localhost:8000/v1/chat/completions" \
-H "Authorization: Bearer han1234" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3.1-flash-image-landscape",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "Transform this image into a watercolor painting"
},
{
"type": "image_url",
"image_url": {
"url": "data:image/jpeg;base64,<base64_encoded_image>"
}
}
]
}
],
"stream": true
}'curl -X POST "http://localhost:8000/v1/chat/completions" \
-H "Authorization: Bearer han1234" \
-H "Content-Type: application/json" \
-d '{
"model": "veo_3_1_t2v_fast_landscape",
"messages": [
{
"role": "user",
"content": "A kitten chasing butterflies across a meadow"
}
],
"stream": true
}'curl -X POST "http://localhost:8000/v1/chat/completions" \
-H "Authorization: Bearer han1234" \
-H "Content-Type: application/json" \
-d '{
"model": "veo_3_1_i2v_s_fast_fl_landscape",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "Transition smoothly from the first image to the second image"
},
{
"type": "image_url",
"image_url": {
"url": "data:image/jpeg;base64,<first_frame_base64>"
}
},
{
"type": "image_url",
"image_url": {
"url": "data:image/jpeg;base64,<last_frame_base64>"
}
}
]
}
],
"stream": true
}'The server assembles the current
R2Vrequest body automatically; callers continue to use OpenAI-compatible input. LandscapeR2Vrequests are mapped to the latest upstream*_landscapemodel key. A request can currently include up to three reference images.
curl -X POST "http://localhost:8000/v1/chat/completions" \
-H "Authorization: Bearer han1234" \
-H "Content-Type: application/json" \
-d '{
"model": "veo_3_1_r2v_fast_portrait",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "Create a portrait video with a smooth camera push based on the characters and setting in these three reference images"
},
{
"type": "image_url",
"image_url": {
"url": "data:image/jpeg;base64/<reference_image_1_base64>"
}
},
{
"type": "image_url",
"image_url": {
"url": "data:image/jpeg;base64/<reference_image_2_base64>"
}
},
{
"type": "image_url",
"image_url": {
"url": "data:image/jpeg;base64/<reference_image_3_base64>"
}
}
]
}
],
"stream": true
}'This project is licensed under the MIT License. See LICENSE for details.
- PearNoDec for the YesCaptcha integration
- raomaiping for the headless CAPTCHA solution
Thanks to every contributor and user for their support.
- Report an issue: GitHub Issues
β If this project helps you, please give it a star!