Images: understand and generate
You'll get a model to answer questions about photos and screenshots, and to draw pictures from a description once generation is back on.
You need: a fetch_... key (create one), a balance (top up), Python and pip install openai.
1Asking about an image
- 1Install the SDK
pip install openai - 2Save the code
Copy the code below into
vision.py. - 3Ask a questionbash
export ZUKKO_API_KEY="fetch_..." python vision.py screenshot.png "What does this error mean and how do I fix it?" python vision.py https://example.com/photo.jpg "Describe this product for a marketplace listing"Pass the image as a file or a public link. The model can't download private or expiring links.
import base64
import mimetypes
import os
import sys
from openai import OpenAI
client = OpenAI(
base_url="https://api.zukko.pro/v1",
api_key=os.environ["ZUKKO_API_KEY"],
timeout=120,
)
MODEL = os.environ.get("ZUKKO_MODEL", "claude-haiku-5-5") # the model must accept images
2Which model to use
The model must accept images — see the "Image input" column in Models & pricing.
| Model | Input / output, $ per 1M tokens | When to use |
|---|---|---|
claude-haiku-5-5 | 0.05 / 0.2 | Receipts, screenshots, simple photos |
gpt-6-sol | 0.13 / 0.63 | All-rounder |
claude-sonnet-5 | 0.4 / 2 | Small print, tables, diagrams |
qwen3.8-omni-flash | 0.03 / 0.08 | Cheapest |
These don't accept images: deepseek-v4.1-flash, gemini-3.8-flash, kimi-k3.1-pro, glm-5.3, gpt-6-luna. Live list: GET /v1/models/info, field supports_vision.
3A receipt into JSON
Put receipt.py next to vision.py and run python receipt.py receipt.jpg. The code checks that the lines add up to the total — review mismatches by hand.
import json
import sys
from vision import client, image_to_data_url
RECEIPT_FORMAT = {
"type": "json_schema",
"json_schema": {
"name": "receipt",
"strict": True,
"schema": {
"type": "object",
"properties": {
"store": {"type": "string"},4In a Telegram bot
Add the handler to the bot from Telegram bot with AI: the user sends a photo with a question in the caption. The model in ZUKKO_MODEL must accept images.
import base64
async def on_photo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
photo = update.message.photo[-1] # Telegram sends several sizes; take the largest
file = await photo.get_file()
data = base64.b64encode(await file.download_as_bytearray()).decode()
question = update.message.caption or "What is in this picture?"
response = await client.chat.completions.create(
model=MODEL, # the model must accept images, e.g. claude-haiku-5-5
messages=[
{
"role": "user",5Generating images
The endpoint is POST /v1/images/generations, OpenAI format. Generation takes up to 3 minutes, so set a 300-second client timeout. The curl version needs jq. All parameters: POST /v1/images/generations.
import base64
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.zukko.pro/v1",
api_key=os.environ["ZUKKO_API_KEY"],
timeout=300, # generation can take up to 3 minutes
)
result = client.images.generate(
model="openai-gpt-image-2-text-to-image",
prompt="A cozy coffee shop on an autumn evening, warm light from the windows, watercolor",
size="1024x1024",
quality="high",
response_format="b64_json",
)
with open("picture.png", "wb") as f:
f.write(base64.b64decode(result.data[0].b64_json))
print("Saved: picture.png")6If it doesn't work
| What you see | What to do |
|---|---|
| 400, or the model "can't see" the image | Use a model that accepts images, e.g. claude-haiku-5-5 |
| An error about the image URL | Download the image and send it as a file (data URL) |
| The model misreads receipt numbers | Retake the photo closer, use claude-sonnet-5 |
json.JSONDecodeError in receipt.py | Retry; raise max_tokens for long receipts |
503 model_disabled when generating | Image models are temporarily off — watch GET /v1/models |
402 insufficient_balance or 504 image_generation_timeout | 402 — top up. 504 — retry, simplify the prompt |
Other codes: API errors.