Image generation is available for every project with no setup needed (user_pays billing by default).
Plan availability: Image generation is limited to 3 uses per month on the Free plan, then unlimited on Pro. When a Free-plan owner exceeds the cap, this endpoint returns 403 FORBIDDEN with an upgrade message - handle that response in the app's UI.
Capability boundary: text-to-image only
This service is text-to-image only: a text prompt in, a brand-new image out. The request has no input-image field - it cannot edit, restyle, inpaint, upscale, or make variations of an existing image, and there are no undocumented parameters that enable that. Don't guess at an image/input_image field; it doesn't exist.
If the app needs to modify an image the user already has, build that capability instead:
- Client-side processing (crops, filters, overlays, compositing) with canvas/WebGL - no server needed.
- A custom editing model on Gipity Jobs - run an instruction-following image-editing model (e.g. InstructPix2Pix) as a GPU job; see the
jobsskill. - Re-generation - describe the desired result and generate a fresh image. Fine for stock-art-style assets; not a substitute when the user's original photo must be preserved.
Use project_settings to customize (optional):
- Switch billing mode (owner_pays ↔ user_pays)
- Restrict allowed providers
- Set default provider/model
To make the billing choice reproducible (it ships with the app instead of living as out-of-band server state), declare it in a
servicesdeploy phase ingipity.yamlinstead of only viaproject_settings- e.g.{ service: image, billing_mode: owner_pays }. See deploy and app-llm.
Providers & Models
- OpenAI:
gpt-image-2 - BFL/Flux:
flux-2-pro, flux-2-flex, flux-2-max, flux-2-klein-9b, flux-2-klein-4b - Gemini/Nano Banana:
gemini-3.1-flash-lite-image, gemini-2.5-flash-image, gemini-3.1-flash-image, gemini-3-pro-image
Quality tiers (recommended over pinning a model)
Instead of a concrete model id, pass a quality tier as model — it resolves to a current provider+model that we keep pointed at the best option, so your app keeps working when the model lineup refreshes (a pinned concrete id 400s the day it's retired). A tier sets the provider for you (overriding provider):
fast— cheapest, fastest; for thumbnails, drafts, high-volume generationstandard— balanced general-purpose quality (the sensible default)high— high fidelity; for hero images and detailed scenesultra— maximum fidelity; slowest and most expensive
{ "prompt": "A sunset over mountains", "model": "high" }
GET /image/models returns the live tier→provider/model map (tiers) alongside the concrete model list. Pin a concrete id only when you specifically need that exact model.
Endpoints
GET /api/<PROJECT_GUID>/services/image/models- list available providers and modelsPOST /api/<PROJECT_GUID>/services/image- generate an image
Request Format (POST /image)
{
"prompt": "A sunset over mountains",
"provider": "openai",
"model": "gpt-image-2",
"size": "1024x1024",
"quality": "auto"
}
Fields:
prompt(required): Image description, max 4,000 charsprovider: "openai", "bfl", or "gemini" (default: bfl)model: A quality tier —fast,standard,high, orultra(recommended; survives model refreshes and sets the provider) — OR a concrete model id (default: provider's default). See Quality tiers above.size: "WxH" format (default: 1024x1024). OpenAI sizes: 1024x1024, 1024x1536, 1536x1024. BFL: any size (rounded to 32px). Gemini: mapped to nearest resolution tierquality: OpenAI gpt-image-2: low/medium/high/autoaspect_ratio: Gemini only. Aspect ratio: 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 4:5, 5:4, 21:9image_size: Gemini only. Resolution tier: 512, 1K, 2K, 4K (default: 1K)seed: BFL only (ignored by openai/gemini). A non-negative integer that makes generation deterministic — reuse one seed across a series of prompts to keep the images visually coherent (e.g. every illustration in one clip). Omit it for fresh randomness each call.
Response Format
{
"url": "https://media.gipity.ai/med_abc12345.png",
"content_type": "image/png",
"revised_prompt": "...",
"model": "gpt-image-2",
"provider": "openai",
"credits_used": 50
}
The url is a permanent public CDN URL. No auth needed to fetch it.
BFL responses also include a seed field — the seed that produced this image (the one you passed, or the random one BFL picked). Capture it and pass it back as seed on later calls to keep a series of images visually coherent.
CLI
For one-off image generation during development (downloads the result to a local file), skip the HTTP call and use gipity generate image. It writes to ./generated.png in the current directory by default - pass -o <path> to land the file directly in your source tree so it deploys:
gipity generate image "a cat wearing a top hat" -o src/assets/images/hat-cat.png
gipity generate image "landscape sunset" --provider gemini --aspect-ratio 16:9 -o src/assets/images/hero.png
gipity generate image "product photo" --provider openai --model gpt-image-2 --size 1536x1024 --quality high -o src/assets/images/product.png
Client Code Example
IMPORTANT: The token endpoint is on the API server. Use https://a.gipity.ai/api/token (POST).
// 1. Get app token
const tokenRes = await fetch('https://a.gipity.ai/api/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ app: '<PROJECT_GUID>' })
});
const { data: { token } } = await tokenRes.json();
// 2. Generate image
const res = await fetch('https://a.gipity.ai/api/<PROJECT_GUID>/services/image', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-App-Token': token },
body: JSON.stringify({ prompt: 'A cat wearing a top hat' })
});
const data = await res.json();
// data.url → "https://media.gipity.ai/med_abc12345.png"
// 3. Display
const img = document.createElement('img');
img.src = data.url;
document.body.appendChild(img);
Limits
- Rate limit: 600 requests per 5-minute window (per IP)
- Max prompt length: 4,000 chars
- Timeout: 120s
- Standard
RateLimit-*headers included in responses