Image generation & editing
Norlen offers three image endpoints: generation (text → image), editing (image + instruction → image) and composition (2–3 references + prompt → new image). All return the image in base64 and follow the spirit of OpenAI’s images API.
Generation (text → image)
Section titled “Generation (text → image)”Generates an image from a text prompt. Follows OpenAI’s images/generations format.
POST https://api.norlen.io/v1/images/generationsAuthorization: Bearer YOUR_API_KEYContent-Type: application/jsonParameters
Section titled “Parameters”| Field | Type | Required | Description |
|---|---|---|---|
model | string | yes | qwen-image (default) or flux (FLUX.1 Kontext — sharper text, subject-preserving edits) |
prompt | string | yes | Description of the desired image |
size | string | no | Dimensions, e.g. 1024x1024 (default and recommended) |
n | integer | no | Number of images (default 1) |
Example
Section titled “Example”curl https://api.norlen.io/v1/images/generations \ -H "Authorization: Bearer $NORLEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "qwen-image", "prompt": "A brass compass on a nautical chart, studio lighting", "size": "1024x1024" }'import base64, requests
r = requests.post( "https://api.norlen.io/v1/images/generations", headers={"Authorization": "Bearer YOUR_TOKEN"}, json={"model": "qwen-image", "prompt": "A brass compass on a nautical chart", "size": "1024x1024"}, timeout=120,)data = r.json()["data"][0]["b64_json"]with open("image.png", "wb") as f: f.write(base64.b64decode(data))const r = await fetch("https://api.norlen.io/v1/images/generations", { method: "POST", headers: { Authorization: `Bearer ${process.env.NORLEN_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "qwen-image", prompt: "A brass compass on a nautical chart", size: "1024x1024" }),});const { data } = await r.json();const buffer = Buffer.from(data[0].b64_json, "base64");await require("fs/promises").writeFile("image.png", buffer);Response
Section titled “Response”{ "created": 1750000000, "data": [ { "b64_json": "iVBORw0KGgoAAAANSUhEUgAA..." } ]}The image comes in data[0].b64_json (base64-encoded PNG). Decode and save it, or display it with data:image/png;base64,<...>.
Editing (image + instruction → image)
Section titled “Editing (image + instruction → image)”Edit an existing image by instruction — describe the change and Norlen applies it while preserving the rest of the scene (Qwen-Image-Edit model). Follows the spirit of OpenAI’s images/edits.
POST https://api.norlen.io/v1/images/editsAuthorization: Bearer YOUR_API_KEYIt accepts two input formats:
multipart/form-data(like the OpenAI SDK): animagefile field plus apromptwith the instruction.- JSON:
{ "image": "<base64 or data URL>", "prompt": "..." }.
Parameters
Section titled “Parameters”| Field | Type | Required | Description |
|---|---|---|---|
image | file | string | yes | The image to edit — a file (multipart) or base64/data URL (JSON). Max ~10MB |
prompt | string | yes | The edit instruction, e.g. “make the apple blue”. Up to 2000 characters |
n | integer | no | Number of variations (1–4, default 1) |
Optional model field: qwen-image (default, Qwen-Image-Edit) or flux
(FLUX.1 Kontext — better subject preservation).
Example
Section titled “Example”curl https://api.norlen.io/v1/images/edits \ -H "Authorization: Bearer $NORLEN_API_KEY" \ -F image=@photo.png \ -F prompt="make the apple blue"import base64, requests
r = requests.post( "https://api.norlen.io/v1/images/edits", headers={"Authorization": "Bearer YOUR_TOKEN"}, files={"image": open("photo.png", "rb")}, data={"prompt": "make the apple blue"}, timeout=180,)open("edited.png", "wb").write(base64.b64decode(r.json()["data"][0]["b64_json"]))import { readFile } from "node:fs/promises";
const form = new FormData();form.append("image", new Blob([await readFile("photo.png")]), "photo.png");form.append("prompt", "make the apple blue");
const r = await fetch("https://api.norlen.io/v1/images/edits", { method: "POST", headers: { Authorization: `Bearer ${process.env.NORLEN_API_KEY}` }, body: form,});const { data } = await r.json();const fs = await import("node:fs/promises");await fs.writeFile("edited.png", Buffer.from(data[0].b64_json, "base64"));curl https://api.norlen.io/v1/images/edits \ -H "Authorization: Bearer $NORLEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"image":"<base64-or-data-url>","prompt":"make the apple blue"}'The response has the same shape as generation: { "created": ..., "data": [{ "b64_json": "..." }] }.
Composition (combine references → new image)
Section titled “Composition (combine references → new image)”Send 2 to 3 reference images along with a prompt and get back a new image that combines elements from the references following your description — e.g. the person from one image holding the product from another, or a character placed inside a scene. Uses the Qwen-Image-Edit 2509 (multi-image) model.
POST https://api.norlen.io/v1/images/composeAuthorization: Bearer YOUR_API_KEYAccepts two input formats:
multipart/form-data: repeat theimagefield for each reference (2–3×) +prompt.- JSON:
{ "images": ["<base64 or data URL>", ...], "prompt": "...", "size": "1024x1024" }.
In the prompt, refer to the images by upload order — “image 1”, “image 2”…
Parameters
Section titled “Parameters”| Field | Type | Required | Description |
|---|---|---|---|
image / images | file[] | string[] | yes | 2 to 3 references — repeated image fields (multipart) or an images array (JSON). Max ~10MB each |
prompt | string | yes | How to combine the references. Up to 2000 chars |
size | string | no | Output dimensions, e.g. 1024x1024 (default) |
n | integer | no | Number of images (1–4, default 1) |
Example
Section titled “Example”curl https://api.norlen.io/v1/images/compose \ -H "Authorization: Bearer $NORLEN_API_KEY" \ -F image=@person.png \ -F image=@product.png \ -F prompt="the person from image 1 holding the product from image 2, studio light" \ -F size=1024x1024import base64, requests
r = requests.post( "https://api.norlen.io/v1/images/compose", headers={"Authorization": "Bearer YOUR_TOKEN"}, files=[ ("image", open("person.png", "rb")), ("image", open("product.png", "rb")), ], data={"prompt": "the person from image 1 holding the product from image 2", "size": "1024x1024"}, timeout=180,)open("composition.png", "wb").write(base64.b64decode(r.json()["data"][0]["b64_json"]))import { readFile } from "node:fs/promises";
const form = new FormData();form.append("image", new Blob([await readFile("person.png")]), "person.png");form.append("image", new Blob([await readFile("product.png")]), "product.png");form.append("prompt", "the person from image 1 holding the product from image 2");
const r = await fetch("https://api.norlen.io/v1/images/compose", { method: "POST", headers: { Authorization: `Bearer ${process.env.NORLEN_API_KEY}` }, body: form,});const { data } = await r.json();const fs = await import("node:fs/promises");await fs.writeFile("composition.png", Buffer.from(data[0].b64_json, "base64"));curl https://api.norlen.io/v1/images/compose \ -H "Authorization: Bearer $NORLEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{"images":["<base64-1>","<base64-2>"],"prompt":"the person from image 1 holding the product from image 2"}'The response has the same shape: { "created": ..., "data": [{ "b64_json": "..." }] }.
Billing
Section titled “Billing”Each plan includes a monthly image quota — it covers generation, editing and composition (each call counts as one image). Images beyond the quota cost $0.03 each, debited from your balance. See Pricing & quotas.