Lewati ke konten
kenari.

Gambar

Buat gambar dari prompt teks, atau edit gambar yang sudah ada dengan prompt. Endpoint ini kompatibel dengan OpenAI Images, jadi SDK resmi OpenAI bisa dipakai dengan base URL https://kenari.id/v1. Hanya model gambar yang dilayani di sini. Mengirim model teks mengembalikan 400.

POST /v1/images/generations

POST /v1/images/edits

Daftar model gambar beserta harganya ada di daftar model:

Terminal window
curl -s https://kenari.id/v1/models \
| jq '.data[] | select(.endpoints | index("images")) | {id, pricing_lines}'

POST /v1/images/generations menerima body JSON.

FieldTipeWajibKeterangan
modelstringyaId model gambar, misalnya gpt-image-2.
promptstringyaDeskripsi gambar.
nintegertidakJumlah gambar, 1 sampai 10. Default 1. Nilai di luar rentang dipindahkan ke batas terdekat, dan kamu ditagih sesuai jumlah itu.
sizestringtidakUkuran gambar, misalnya "1024x1024". Berupa petunjuk, bukan jaminan. Lihat Ukuran gambar.
backgroundstringtidak"transparent", "opaque" atau "auto". Nilai lain mengembalikan 400. Apakah gambar benar-benar punya latar transparan bergantung pada modelnya.
response_formatstringtidak"url" atau "b64_json". Dukungannya berbeda per model, jadi jangan kirim field response_format agar default model digunakan. Lihat Response.

POST /v1/images/edits mengubah gambar yang sudah ada sesuai prompt. Request-nya multipart/form-data karena gambar dikirim sebagai file. Ulangi field image (atau image[]) untuk setiap file sumber.

FieldTipeWajibKeterangan
modelstringyaId model gambar, misalnya gpt-image-2.
imagefileyaGambar sumber. Kirim PNG, JPEG atau WebP. Ulangi field-nya untuk maksimal 16 file, masing-masing sampai 25 MiB.
promptstringyaDeskripsi perubahan. Maksimal 8.192 byte.
maskfiletidakMask PNG, maksimal 25 MiB. Seperti di OpenAI Images, area transparan menandai bagian yang diedit. Tanpa mask, seluruh gambar bisa berubah.
nintegertidakJumlah gambar hasil, 1 sampai 10. Default 1. Tidak bergantung pada jumlah file sumber.
sizestringtidakUkuran gambar, misalnya "1024x1024". Berupa petunjuk, bukan jaminan. Lihat Ukuran gambar.
backgroundstringtidak"opaque" atau "auto". "transparent" tidak didukung pada edit dan mengembalikan 400. Pakai generations untuk latar transparan.
response_formatstringtidak"url" atau "b64_json". Catatannya sama dengan generations.

Seluruh body request boleh sampai 32 MiB. Gambar ke-17, file yang melewati batasnya, atau body di atas 32 MiB ditolak sebelum ada biaya yang terpotong. Sebagian model hanya menerima satu gambar sumber atau tidak mendukung mask. Model seperti itu menjawab 400 kalau kamu mengirim lebih.

Kedua endpoint mengembalikan bentuk OpenAI Images: created dan array data dengan satu item per gambar.

{
"created": 1718700000,
"data": [
{ "b64_json": "iVBORw0KGgoAAAANSUhEUg..." }
]
}

Setiap item berisi b64_json, url, atau keduanya, tergantung model dan response_format. url berupa data: URI yang memuat gambar di dalamnya, bukan tautan ke file yang di-hosting. Baca field mana pun yang ada, lalu decode bagian base64-nya. Response juga bisa memuat usage, size, quality, output_format, dan revised_prompt per gambar kalau modelnya melaporkannya.

Model memperlakukan size dengan tiga cara. Ada yang memakainya apa adanya, ada yang mengabaikannya, dan ada yang hanya mengenal rasio aspek. Untuk model berbasis rasio, kenari memetakan ukuran yang kamu kirim ke rasio terdekat yang didukung model, jadi "1792x1024" menghasilkan gambar 16:9 dengan jumlah piksel yang ditentukan model, bukan persis 1792x1024. Bentuk gambar selalu sedekat mungkin dengan permintaan, tetapi jumlah pikselnya bisa berbeda. Kalau ukuran piksel yang tepat itu penting, periksa dimensi gambar yang dikembalikan.

Pembuatan gambar bisa memakan beberapa menit. Kalau hasilnya belum siap dalam 45 detik, kenari langsung mengirim status 200 dan header, lalu menulis satu spasi setiap 20 detik agar proxy tidak memutus koneksi, dan menutup dengan body JSON. Spasi di awal tetap JSON yang valid, jadi parse body lengkap seperti biasa. Atur timeout klien minimal 5 menit.

Terminal window
curl https://kenari.id/v1/images/generations \
-H "Authorization: Bearer $KENARI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2",
"prompt": "Seekor panda merah sedang menulis kode di laptop",
"n": 1,
"size": "1024x1024"
}' \
--max-time 300 \
--output response.json
if jq -e '.error' response.json > /dev/null; then
jq -r '.error.message' response.json >&2
else
jq -r '.data[0] | .b64_json // (.url | split(",")[1])' response.json \
| base64 -d > image.png
fi
import base64
import os
from openai import OpenAI
client = OpenAI(
base_url="https://kenari.id/v1",
api_key=os.environ["KENARI_API_KEY"],
)
result = client.images.generate(
model="gpt-image-2",
prompt="Seekor panda merah sedang menulis kode di laptop",
n=1,
size="1024x1024",
)
error = getattr(result, "error", None)
if error or not result.data:
raise RuntimeError(f"Image request failed: {error}")
item = result.data[0]
b64 = item.b64_json or item.url.split(",", 1)[1]
with open("image.png", "wb") as f:
f.write(base64.b64decode(b64))
import fs from "node:fs";
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://kenari.id/v1",
apiKey: process.env.KENARI_API_KEY,
});
const result = await client.images.generate({
model: "gpt-image-2",
prompt: "Seekor panda merah sedang menulis kode di laptop",
n: 1,
size: "1024x1024",
});
if (result.error || !result.data?.length) {
throw new Error(`Image request failed: ${JSON.stringify(result.error)}`);
}
const item = result.data[0];
const b64 = item.b64_json ?? item.url.split(",")[1];
fs.writeFileSync("image.png", Buffer.from(b64, "base64"));
Terminal window
curl https://kenari.id/v1/images/edits \
-H "Authorization: Bearer $KENARI_API_KEY" \
-F "model=gpt-image-2" \
-F "image=@source.png" \
-F "image=@reference.png" \
-F "prompt=Ganti langit menjadi senja, dengan gaya gambar kedua" \
-F "n=1" \
--max-time 300 \
--output response.json
if jq -e '.error' response.json > /dev/null; then
jq -r '.error.message' response.json >&2
else
jq -r '.data[0] | .b64_json // (.url | split(",")[1])' response.json \
| base64 -d > edited.png
fi
import base64
import os
from openai import OpenAI
client = OpenAI(
base_url="https://kenari.id/v1",
api_key=os.environ["KENARI_API_KEY"],
)
result = client.images.edit(
model="gpt-image-2",
image=[open("source.png", "rb"), open("reference.png", "rb")],
prompt="Ganti langit menjadi senja, dengan gaya gambar kedua",
n=1,
)
error = getattr(result, "error", None)
if error or not result.data:
raise RuntimeError(f"Image request failed: {error}")
item = result.data[0]
b64 = item.b64_json or item.url.split(",", 1)[1]
with open("edited.png", "wb") as f:
f.write(base64.b64decode(b64))
import fs from "node:fs";
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://kenari.id/v1",
apiKey: process.env.KENARI_API_KEY,
});
const result = await client.images.edit({
model: "gpt-image-2",
image: [fs.createReadStream("source.png"), fs.createReadStream("reference.png")],
prompt: "Ganti langit menjadi senja, dengan gaya gambar kedua",
n: 1,
});
if (result.error || !result.data?.length) {
throw new Error(`Image request failed: ${JSON.stringify(result.error)}`);
}
const item = result.data[0];
const b64 = item.b64_json ?? item.url.split(",")[1];
fs.writeFileSync("edited.png", Buffer.from(b64, "base64"));

Gambar ditagih per gambar: n dikali harga per gambar model, diambil dari saldo kamu. Empat gambar berharga empat kali satu gambar, dan jumlah file sumber pada edit tidak mengubah harga. Jumlahnya ditahan saat request diterima. Kamu membayar gambar yang benar-benar terkirim, jadi provider yang mengembalikan kurang dari n gambar membuat biayanya lebih kecil, dan response tanpa gambar yang bisa dipakai dianggap gagal dan melepas seluruh tahanan. Selengkapnya ada di Cara penagihan.

StatusKodeKapan
400bad_requestModel bukan model gambar, prompt, model atau image tidak ada, background tidak valid, atau edit memuat lebih dari 16 gambar, file di atas 25 MiB, atau prompt di atas 8.192 byte.
402insufficient_balanceSaldo tidak cukup untuk n gambar.
413tidak adaBody request di atas 32 MiB. Response-nya teks biasa.
503all_providers_failed, upstream_errorTidak ada provider yang bisa menghasilkan gambar. Tidak ada biaya yang terpotong. Coba lagi.

Kode lainnya ada di Error.