# gemini-business2api runs a foreign latest image and leaves its refresh worker out by default

> A Python gateway that puts an OpenAI-compatible surface in front of Gemini Business, with an admin panel and optional account refresh. The compose file pulls a tagged image from a different account rather than building the code in the tree, and the worker that keeps sessions alive sits behind a profile flag.

**yukkcat/gemini-business2api** — OpenAI-compatible API for Gemini Business with multi-account load balancing and multimodal capabilities (image/video generation, file parsing) | 将 Gemini Business 转为 OpenAI 兼容接口，支持多账户负载均衡及多模态能力（图像生成、视频生成、解析文件）

- Repository: https://github.com/yukkcat/gemini-business2api
- Website: https://gemini-business2api.nanohajimi.mom
- Stars: 1,328 · Forks: 908
- Language: Python
- License: NOASSERTION
- Published: 2026-09-14 · Updated: 2026-09-14 · Language: en
- Canonical page: https://hysenlabs.com/projects/yukkcat-gemini-business2api

## The compose file runs an image from another account, tagged latest

The quick start clones the repository, copies the example environment file, and runs one command. What that command does is not build the code you just cloned:

```
git clone https://github.com/yukkcat/gemini-business2api.git
cd gemini-business2api
cp .env.example .env
docker compose up -d
```

```
image: cooooookk/gemini-business2api:latest
```

There is no build key in either service, only an image reference. The repository under discussion is yukkcat/gemini-business2api, while the image is published by cooooookk. The optional worker is the same story:

```
image: ${REFRESH_WORKER_IMAGE:-cooooookk/gemini-refresh-worker:latest}
```

So the deployed artefact is whatever a second account last pushed under the moving tag, and the 1328 stars on this repository describe the documentation and the compose file rather than the running binary. This is a normal pattern for a project that publishes images separately, and it is also the single fact a reader should check before typing the first command. The Dockerfile in the tree builds fine if you invoke it yourself, in two stages on node:20-slim then python:3.11-slim, ending at CMD ["python", "-u", "main.py"]. Nothing in the compose file asks for it.

## The header names v0.3.3 as stable while the newest listed release is v0.3.0

The README header states a current stable version of v0.3.3 and links to that release tag. The release feed for the repository lists v0.3.0, published 2026-04-24, as the most recent release. The two do not agree, and the repository keeps a VERSION file at the top level, which means three sources could each be authoritative and no single one is marked as such.

This matters mostly because the install script offers a pinned variant alongside the floating one:

```
curl -fsSL https://raw.githubusercontent.com/yukkcat/gemini-business2api/v0.3.3/deploy/install.sh | sudo bash
```

versus the same command against the main branch. If the tag named in the pinned URL is not the tag the release feed advertises, then the pinned install is pinning to something a reader cannot discover from the releases page. The discrepancy is small and looks administrative, but a version claim that a machine has to resolve is worth verifying by hand.

The last push to the repository is dated 21 May 2026, and there are 37 open issues.

## Configuration lives in three places and the panel is not always the last word

The feature list promises a single settings surface that centralises proxy, mail, refresh and output format configuration. The example environment file describes something more layered, and it is worth reading as the precedence rules rather than as a list of keys.

The first location is the admin panel's system settings, which hold shared configuration for the main service, shared refresh configuration and mail defaults. The second is the panel's account management screen, which holds per-account fields including mail_provider, mail_address, mail_password, mail_refresh_token, mail_base_url and mail_verify_ssl. The third is the worker's own environment, described as a machine-local override to be used only when it is actually needed.

The fallback direction is the interesting part. When the worker's environment variables are left blank, the worker reads the shared values back out of the panel. That makes the panel the source of truth by default. The exception is documented explicitly:

```
# FORCE_REFRESH_ENABLED=true
```

This one is stated to take priority over the panel setting. So a single flag can flip refresh on or off from outside the application, which is convenient during incident response and is exactly the kind of override that gets forgotten and then argues with a colleague six months later.

## Image generation is optional on four chat models and required on a fifth

The capability table distinguishes dedicated generation models from the chat models, and the difference is not cosmetic. `gemini-auto`, `gemini-2.5-pro`, `gemini-3.5-flash` and `gemini-3.1-pro-preview` all share the same profile: image understanding, native web access and file multimodal input, with image generation marked optional and video generation absent. `gemini-imagen` is the dedicated image generation model, and `gemini-veo` is the dedicated video generation model.

Read as a routing table, that means a request for image generation against a chat model ID is not an error, it is an optional capability, and a request against `gemini-veo` is the only supported path for video. An OpenAI-compatible client that assumes every model can do everything will get inconsistent behaviour across model IDs without an obvious error, which is the usual failure mode of a compatibility layer.

The endpoint list is correspondingly short: a GET on /v1/models, a POST on /v1/chat/completions, POST routes for /v1/images/generations and /v1/images/edits, and a GET on /health. There is no video endpoint in that table, so video generation is reachable through the image and chat surface rather than through a dedicated compatibility route.

## The refresh worker is a profile that must be asked for by name

Since v0.3.0 the project has restructured itself around a single main service. The registration tooling, the registration flow, the refresh executor that used to be embedded in the main service, and the older chain that depended on a browser display environment have all been removed or moved out. Refresh capability is now something you attach from outside.

Mechanically that attachment is a compose profile. The worker is declared with a refresh profile, so it does not start unless you name it, and the README is explicit that the flag is not a separate install flow:

```
docker compose --profile refresh up -d
```

The worker also refuses to race the main service. Its dependency on the gateway is a healthy condition, meaning the API's own healthcheck has to pass first, and the healthcheck runs curl against /health every 30 seconds with three retries and a ten-second start period. The worker publishes no business API; its port is a health port that defaults to 8080, and both services mount the same ./data directory.

Removing the registration path is the more consequential decision of the two. A gateway that also automates account creation is a different category of software from one that manages accounts you already have, and the project chose to leave that category.

## SQLite is the default, and the worker is handed a hardcoded path to it

Storage configuration is one line. Leave DATABASE_URL unset and the service uses a local SQLite file, which the README recommends; set it to a PostgreSQL URL and the service switches over. The example file shows the shape:

```
# DATABASE_URL=postgresql://user:password@host:5432/dbname?sslmode=require
```

The default SQLite location is ./data/data.db, overridable through SQLITE_PATH. The worker does not get that freedom in the compose file, where its path is fixed:

```
SQLITE_PATH: /app/data/data.db
```

Because both containers mount ./data at /app/data, that hardcoded path is consistent, and it is also the reason the compose file cannot put the worker on a remote database while the main service stays local without overriding the environment. PostgreSQL support is real rather than aspirational: asyncpg is the one entry in requirements.txt held back as optional, with a comment saying to uncomment it and set DATABASE_URL where persistent storage is unavailable.

Log rotation is configured the same way for both services, capping json-file output at ten megabytes with three files kept. For a service that logs request and account activity, that is a small ring, and it is the kind of value that is comfortable until an incident needs the older entries.

## The account import helper asks for full cookie access to a workspace page

There is an optional userscript for pulling account records out of the Gemini Business page. Installing it from the repository requires granting a script broad access: Tampermonkey's general configuration mode set to Advanced, its security setting for cookie access set to All, and, if cookies still are not reachable, developer mode enabled in the browser extension page. The script detects the missing permission and pops up a reminder.

Once it has access, clicking Copy JSON puts the export on the clipboard and a shifted click downloads it as a file. One exported field deserves attention: expires_at defaults to the current time plus twelve hours, so every account in an import batch expires within the same twelve-hour window unless you edit it.

The trade-off is real and worth stating plainly. This is a script from a third-party repository, fetched over a raw content URL, that is granted unrestricted cookie access to a signed-in Google workspace domain. That is a large grant for a convenience feature. It is also the only documented path for bulk import, and the project does not offer a first-party alternative in the tree.

## The licence is a custom non-commercial one the tooling cannot classify

The README states the project uses the Cooperative Non-Commercial License, version CNC-1.0, and a LICENSE file sits at the top level. The repository's own licence metadata resolves to no recognised identifier, which is what happens when an automated classifier meets a licence it has not seen before.

For a gateway sitting in front of a paid business product, that combination deserves a decision rather than a shrug. A non-commercial restriction and the surrounding terms of a bespoke licence are the kind of thing that has to be read before the service is pointed at company data, and the missing identifier means no automated scan in your pipeline will flag it either way.

The dependency list is otherwise conventional and mostly pinned exactly, with fastapi at 0.115.0, uvicorn at 0.32.0, httpx and requests both carrying socks extras for proxy support, and pydantic at 2.10.0. Only pyyaml and jinja2 use open-ended lower bounds. A separate Chinese-language README is linked from the header for the audience this is aimed at.

## Conclusion

gemini-business2api is a competent piece of gateway engineering for anyone who already runs Gemini Business accounts and wants a single OpenAI-shaped endpoint in front of them. Three things have to be settled before you rely on it. The compose file executes an image published by a different account, so pin a digest or build from the tree. The refresh worker is optional and off by default, which is deliberate. And the licence is a custom non-commercial one that the repository's own metadata cannot classify, so read it before this goes near anything commercial.

## FAQ

### What does gemini-business2api actually expose?

It presents Gemini Business through an OpenAI-compatible surface on port 7860, with a GET on /v1/models, a POST on /v1/chat/completions, POST routes for /v1/images/generations and /v1/images/edits, and a GET on /health. An admin panel is served from the same port.

### Do I need the refresh worker to run gemini-business2api?

No. Since v0.3.0 the main line keeps only the 2API service, the admin panel and an optional refresh worker, and a plain docker compose up -d starts the gateway alone. The worker only starts when you pass the refresh profile, and it waits for the gateway healthcheck to pass first.

### Where do I set the configuration for gemini-business2api?

Shared settings live in the admin panel's system settings screen, per-account mail and provider fields live in account management, and worker-specific overrides go in the worker's own environment variables. Only one flag, FORCE_REFRESH_ENABLED, is stated to take priority over the panel setting.

### Does gemini-business2api need a database server?

No. With DATABASE_URL unset it uses a local SQLite file at ./data/data.db, which the README recommends. Setting DATABASE_URL to a PostgreSQL URL switches it over, and asyncpg is the optional dependency held back in requirements.txt for that case.

### Which models can generate images and video through gemini-business2api?

gemini-imagen is the dedicated image generation model and gemini-veo is the dedicated video generation model. The four chat models, gemini-auto, gemini-2.5-pro, gemini-3.5-flash and gemini-3.1-pro-preview, all handle image understanding, web access and file input, with image generation marked as optional.

## Sources

- [Issues](https://github.com/yukkcat/gemini-business2api/issues)
- [Project website](https://gemini-business2api.nanohajimi.mom)
- [README](https://github.com/yukkcat/gemini-business2api/blob/main/README.md)
- [Releases](https://github.com/yukkcat/gemini-business2api/releases)
- [yukkcat/gemini-business2api on GitHub](https://github.com/yukkcat/gemini-business2api)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/yukkcat-gemini-business2api
