Images with the OpenAI SDK
images.generate and images.edit, unchanged, against our base URL — and the handful of places we answer differently.
What you change
Two lines: the base URL and the key. Then a model id from the catalogue, because we do not serve OpenAI’s image models and will not pretend to — a request for one is a 404 listing what we do have, rather than something else quietly generating the picture.
from openai import OpenAI
client = OpenAI(
base_url="https://api.tokenify.dev/v1",
api_key=os.environ["TOKENIFY_API_KEY"],
)
res = client.images.generate(
model="bytedance/seedream-5.0-pro",
prompt="A still life of three pears, soft window light",
size="2K",
)
open("out.jpg", "wb").write(base64.b64decode(res.data[0].b64_json))import OpenAI from "openai"
const client = new OpenAI({
baseURL: "https://api.tokenify.dev/v1",
apiKey: process.env.TOKENIFY_API_KEY,
})
const res = await client.images.generate({
model: "bytedance/seedream-5.0-pro",
prompt: "A still life of three pears, soft window light",
size: "2K",
})
writeFileSync("out.jpg", Buffer.from(res.data[0].b64_json, "base64"))response_format unless you set one, and we answer base64 when it is absent — which is what the SDK expects, so res.data[0].b64_json is populated with no changes on your side.Editing
client.images.edit(...) posts multipart form data with the image as a file part, and POST https://api.tokenify.dev/v1/images/edits takes it. Several images go as image[], which is what both SDKs send for a list, and they keep the order you gave them.
res = client.images.edit(
model="bytedance/seedream-5.0-pro",
image=[open("subject.png", "rb"), open("style.png", "rb")],
prompt="Put the subject in the style of the second image",
size="2K",
)There is no separate editing model: these models edit by being handed the image alongside the prompt, so an edit is a generation with references. That also means the reference images are charged the way references are charged — see the price table.
Where we answer differently
| OpenAI | Here | Why |
|---|---|---|
mask | 400 | These models have no masked editing. Ignoring a mask would repaint the whole picture and bill for it, so it is refused by name. |
response_format | Honoured | OpenAI's image models always return base64 and ignore this field. Ours takes url as well, and the link is ours, kept 7 days. |
n | 1 to 4 | OpenAI allows up to 10. Above what the model makes is a 400 rather than a clamp, because n decides what you are charged. |
size | Tiers or a range | Different models, different sizes. A tier name lets the model pick the shape from your prompt; the range is per model and published in /v1/models. |
quality | Accepted, ignored | Nothing here varies by quality setting, so it is accepted to keep the SDK call unchanged and listed as ignored rather than pretended. |
stream | 400 | Not yet. The reply is one JSON document; a request for a stream used to produce a bill and no image. |
background | On some models | Transparent backgrounds need a model that does them and output_format: "png". On a model without it, a 400 rather than an opaque image at full price. |
| Unknown fields | 400 | Named in error.param. A parameter dropped on the floor is a request that cost what you expected and did something else. |
The full lists — what works, what is accepted and ignored, what is refused — are on the image generation page.
Errors
The envelope is OpenAI’s, so the SDK’s own exception types work unchanged: {"error": {"message", "type", "code", "param"}}. param names the field when there is one, which is what an SDK surfaces in the exception message. Statuses and codes are on the errors page.
Last updated 2026-10-12.