Image generation
kenari provides an image generation endpoint compatible with OpenAI Images. This endpoint only serves models marked as image models in the catalog. Calling it with a text model returns status 400.
Endpoint
Section titled “Endpoint”POST /v1/images/generations
Send prompt and model, and the gateway returns an image as a URL or base64 data. Cost is deducted from the Rupiah balance per image.
Request parameters
Section titled “Request parameters”| Field | Type | Required | Description |
|---|---|---|---|
model | string | yes | Image model id from the catalog, for example gpt-image-2. |
prompt | string | yes | Description of the requested image. |
n | integer | no | Number of images. Default 1. |
size | string | no | Image size, for example "1024x1024". A hint, not a guarantee: see the note below. |
background | string | no | "transparent", "opaque", or "auto". "transparent" returns a PNG with a transparent background (alpha channel). |
response_format | string | no | "url" or "b64_json". |
Models treat size differently. Some take it as given, some ignore it, and some
understand only aspect ratios, so the size you send is mapped to the nearest
ratio they support. Sending "1792x1024" to a ratio-based model returns a 16:9
image at whatever pixel count that model produces, not exactly 1792x1024. The
shape always lands as close to the request as the model allows; the pixel count
may differ. If exact pixel dimensions matter, check the returned image.
Billing
Section titled “Billing”Billing is calculated per image: n multiplied by the model’s price per image, then deducted from the Rupiah balance. Requesting four images costs four times the price of one image. See Billing for balance and deduction details.
Response shape
Section titled “Response shape”The response follows the OpenAI Images shape: a created field and a data array. Each data element contains url (when response_format is "url") or b64_json (when it is "b64_json").
{ "created": 1718700000, "data": [ { "url": "https://..." } ]}Long jobs and latency
Section titled “Long jobs and latency”Image generation can take several minutes. While the job is running, the gateway keeps the connection open and sends a heartbeat every 20 seconds so proxies on the path do not close a connection they consider idle.
Examples
Section titled “Examples”curl https://kenari.id/v1/images/generations \ -H "Authorization: Bearer kn-..." \ -H "Content-Type: application/json" \ -d '{"model":"gpt-image-2","prompt":"seekor panda merah sedang menulis kode","n":1,"size":"1024x1024"}'Python (OpenAI SDK)
Section titled “Python (OpenAI SDK)”from openai import OpenAI
client = OpenAI( base_url="https://kenari.id/v1", api_key="kn-...",)
result = client.images.generate( model="gpt-image-2", prompt="seekor panda merah sedang menulis kode", n=1, size="1024x1024",)
print(result.data[0].url)JavaScript (OpenAI SDK)
Section titled “JavaScript (OpenAI SDK)”import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://kenari.id/v1", apiKey: "kn-...",});
const result = await client.images.generate({ model: "gpt-image-2", prompt: "seekor panda merah sedang menulis kode", n: 1, size: "1024x1024",});
console.log(result.data[0].url);Image edits (image-to-image)
Section titled “Image edits (image-to-image)”POST /v1/images/edits
This endpoint edits an existing image: send one or more source images, a description of the requested change, and an image model from the catalog. Repeat the image (or image[]) field for each file, up to 16. A 17th file is rejected with 400. Unlike image generation, the request is multipart/form-data, not JSON, because the images are sent as files. Each file can be up to 25 MiB, and the whole body up to 32 MiB. If the model cannot use that many references, the request fails with 503 and every file stays on the request.
Request parameters
Section titled “Request parameters”| Field | Type | Required | Description |
|---|---|---|---|
image | file | yes | One or more source images (PNG, JPEG, or WebP). Repeat the field, up to 16 files. |
prompt | string | yes | Description of the requested edit. |
model | string | yes | Image model id from the catalog, for example gpt-image-2. |
mask | file | no | One PNG mask. Transparent regions mark where the edit applies. Without a mask, the whole image is a candidate for editing. |
n | integer | no | Number of output images. Default 1. Independent of how many source files you send. |
size | string | no | Image size, for example "1024x1024". A hint, not a guarantee: see the note below. |
background | string | no | "opaque" or "auto". "transparent" is not supported on edits yet and is rejected with a 400. Use generation for transparent backgrounds. |
response_format | string | no | "url" or "b64_json". |
Billing and the response shape are identical to image generation above: n times the model’s per-image price, not per source file, deducted from the Rupiah balance, response {created, data:[...]}.
Examples
Section titled “Examples”curl https://kenari.id/v1/images/edits \ -H "Authorization: Bearer kn-..." \ -F "image=@source.png" \ -F "image=@reference.png" \ -F "model=gpt-image-2" \ -F "prompt=change the sky to sunset, using the style of the second image" \ -F "n=1"from openai import OpenAI
client = OpenAI( base_url="https://kenari.id/v1", api_key="kn-...",)
result = client.images.edit( model="gpt-image-2", image=[open("source.png", "rb"), open("reference.png", "rb")], prompt="change the sky to sunset, using the style of the second image", n=1,)
print(result.data[0].url)