curl --request POST \
--url https://api.apiyi.com/v1/images/edits \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form model=grok-imagine-image \
--form 'prompt=Change the scarf color to bright RED. Keep everything else exactly the same.' \
--form image='@example-file'import requests
url = "https://api.apiyi.com/v1/images/edits"
files = { "image": ("example-file", open("example-file", "rb")) }
payload = {
"model": "grok-imagine-image",
"prompt": "Change the scarf color to bright RED. Keep everything else exactly the same."
}
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('model', 'grok-imagine-image');
form.append('prompt', 'Change the scarf color to bright RED. Keep everything else exactly the same.');
form.append('image', '<string>');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://api.apiyi.com/v1/images/edits', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.apiyi.com/v1/images/edits",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ngrok-imagine-image\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nChange the scarf color to bright RED. Keep everything else exactly the same.\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.apiyi.com/v1/images/edits"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ngrok-imagine-image\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nChange the scarf color to bright RED. Keep everything else exactly the same.\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.apiyi.com/v1/images/edits")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ngrok-imagine-image\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nChange the scarf color to bright RED. Keep everything else exactly the same.\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.apiyi.com/v1/images/edits")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ngrok-imagine-image\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nChange the scarf color to bright RED. Keep everything else exactly the same.\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"created": 0,
"data": [
{
"url": "https://apac.ossforai.com/2026/08/12/09b026d5-3492-4678-907c-e25972e6c914.jpg",
"b64_json": "<string>"
}
],
"usage": {
"prompt_tokens": 1000,
"total_tokens": 1000
}
}Image Editing API Reference
Grok Imagine 2 image editing API reference and live testing — upload 1-3 reference images plus an instruction for editing or fusion; requires multipart/form-data
curl --request POST \
--url https://api.apiyi.com/v1/images/edits \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form model=grok-imagine-image \
--form 'prompt=Change the scarf color to bright RED. Keep everything else exactly the same.' \
--form image='@example-file'import requests
url = "https://api.apiyi.com/v1/images/edits"
files = { "image": ("example-file", open("example-file", "rb")) }
payload = {
"model": "grok-imagine-image",
"prompt": "Change the scarf color to bright RED. Keep everything else exactly the same."
}
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('model', 'grok-imagine-image');
form.append('prompt', 'Change the scarf color to bright RED. Keep everything else exactly the same.');
form.append('image', '<string>');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://api.apiyi.com/v1/images/edits', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.apiyi.com/v1/images/edits",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ngrok-imagine-image\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nChange the scarf color to bright RED. Keep everything else exactly the same.\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.apiyi.com/v1/images/edits"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ngrok-imagine-image\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nChange the scarf color to bright RED. Keep everything else exactly the same.\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.apiyi.com/v1/images/edits")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ngrok-imagine-image\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nChange the scarf color to bright RED. Keep everything else exactly the same.\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.apiyi.com/v1/images/edits")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\ngrok-imagine-image\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nChange the scarf color to bright RED. Keep everything else exactly the same.\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"created": 0,
"data": [
{
"url": "https://apac.ossforai.com/2026/08/12/09b026d5-3492-4678-907c-e25972e6c914.jpg",
"b64_json": "<string>"
}
],
"usage": {
"prompt_tokens": 1000,
"total_tokens": 1000
}
}Bearer sk-xxx), choose an image file, fill in prompt and model, and send.multipart/form-data file uploadSending JSON to /v1/images/edits always returns 400:request Content-Type isn't multipart/form-data
{"image": {"type": "image_url", "url": "..."}}), and that form does not work through the APIYI gateway. Follow this page instead.The upside: file upload means no image hosting required — just send the local file, which is simpler than preparing a public URL.The file field must be named image or image[]; images / image_file return 415. prompt is required — omitting it returns 400.resolution and aspect_ratio are accepted here without error but have no effect — the edited output always matches the input reference image’s dimensions (1280x720 in gives 1280x720 out; 1024x1024 in gives 1024x1024 out).To change the output size, crop or resize the reference image before uploading.image[] accepts 1-3 reference images, and the upload order is what “image 1 / image 2 / image 3” refers to in the prompt. State it explicitly, e.g. “put the subject from image 1 into the scene from image 2, keeping image 2’s art style”.Code Examples
Python (OpenAI SDK, single image)
from openai import OpenAI
import urllib.request
client = OpenAI(
api_key="sk-your-api-key",
base_url="https://api.apiyi.com/v1",
timeout=360.0
)
# The SDK's images.edit already performs a multipart upload — pass the file object directly
resp = client.images.edit(
model="grok-imagine-image",
image=open("fox.jpg", "rb"),
prompt="Change the scarf color to bright RED. Keep everything else exactly the same.",
n=1
)
urllib.request.urlretrieve(resp.data[0].url, "edited.jpg")
Python (raw requests, single image)
import requests
import urllib.request
API_KEY = "sk-your-api-key"
# Key point: use files= so requests sets multipart/form-data and the boundary for you.
# Never use json= — that sends application/json and the gateway rejects it with 400.
with open("fox.jpg", "rb") as fp:
response = requests.post(
"https://api.apiyi.com/v1/images/edits",
headers={"Authorization": f"Bearer {API_KEY}"}, # do NOT set Content-Type manually
data={
"model": "grok-imagine-image",
"prompt": "Change the scarf to red, keep everything else exactly the same",
"n": 1,
"response_format": "url"
},
files={"image": ("fox.jpg", fp, "image/jpeg")},
timeout=360
).json()
urllib.request.urlretrieve(response["data"][0]["url"], "edited.jpg")
Python (multi-image fusion, 1-3 files)
import requests
API_KEY = "sk-your-api-key"
# Repeat the image[] field for multiple references — order is "image 1 / image 2"
files = [
("image[]", ("character.jpg", open("character.jpg", "rb"), "image/jpeg")),
("image[]", ("scene.jpg", open("scene.jpg", "rb"), "image/jpeg")),
]
response = requests.post(
"https://api.apiyi.com/v1/images/edits",
headers={"Authorization": f"Bearer {API_KEY}"},
data={
"model": "grok-imagine-image",
"prompt": "Put the character from image 1 into the scene from image 2, "
"keeping image 2's art style and palette",
"response_format": "url"
},
files=files,
timeout=360
).json()
print(response["data"][0]["url"])
cURL
# Single-image edit: -F means multipart/form-data, @ uploads a local file
curl -X POST "https://api.apiyi.com/v1/images/edits" \
-H "Authorization: Bearer sk-your-api-key" \
-F "model=grok-imagine-image" \
-F "prompt=Change the scarf to red, keep everything else exactly the same" \
-F "n=1" \
-F "response_format=url" \
-F "[email protected]"
# Multi-image fusion: repeat image[], order is "image 1 / image 2"
curl -X POST "https://api.apiyi.com/v1/images/edits" \
-H "Authorization: Bearer sk-your-api-key" \
-F "model=grok-imagine-image-quality" \
-F "prompt=Put the character from image 1 into the scene from image 2, keep image 2's style" \
-F "image[][email protected]" \
-F "image[][email protected]"
Node.js (native fetch + FormData)
import fs from 'node:fs';
const form = new FormData();
form.append('model', 'grok-imagine-image');
form.append('prompt', 'Change the scarf to red, keep everything else exactly the same');
form.append('n', '1');
form.append('response_format', 'url');
// Single image uses `image`; for fusion append `image[]` repeatedly (max 3)
form.append('image', new Blob([fs.readFileSync('./fox.jpg')]), 'fox.jpg');
const resp = await fetch('https://api.apiyi.com/v1/images/edits', {
method: 'POST',
// Do not set Content-Type manually — let FormData supply the boundary
headers: { 'Authorization': 'Bearer sk-your-api-key' },
body: form,
signal: AbortSignal.timeout(360000)
});
const data = await resp.json();
const img = await fetch(data.data[0].url);
fs.writeFileSync('edited.jpg', Buffer.from(await img.arrayBuffer()));
Browser JavaScript
// ⚠️ Demo only: a front-end key is exposed — use a backend proxy in production
const fileInput = document.querySelector('#file'); // an <input type="file"> element
const form = new FormData();
form.append('model', 'grok-imagine-image');
form.append('prompt', 'Replace the background with a snowy pine forest at night, keep the subject');
form.append('response_format', 'url');
form.append('image', fileInput.files[0]);
const resp = await fetch('https://api.apiyi.com/v1/images/edits', {
method: 'POST',
headers: { 'Authorization': 'Bearer sk-your-api-key' },
body: form
});
const data = await resp.json();
document.querySelector('#preview').src = data.data[0].url;
Parameter Reference
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
model | string | ✅ | — | grok-imagine-image ($0.02/image) or grok-imagine-image-quality ($0.045/image) |
prompt | string | ✅ | — | Editing instruction. State what changes and that everything else stays put |
image | file | ✅ | — | Reference image file. For fusion repeat image[], 1-3 files |
n | integer | ❌ | 1 | Output images, 1-10, billed per image, independent of reference count |
response_format | string | ❌ | url | url returns a direct link; b64_json returns raw base64 (no data: prefix) |
resolution | string | ❌ | — | No effect here — output follows the input image |
aspect_ratio | string | ❌ | — | No effect here — output follows the input image |
Editing Behaviour and Prompt Style
The editing endpoint preserves the input image’s art style, composition, palette and subject identity, changing only what the prompt specifies. For stable results:| Prompt style | Result |
|---|---|
✅ Change the scarf to red, keep everything else exactly the same | Only the scarf changes; style, composition and background are preserved |
✅ Add round black sunglasses to the cat, keep everything else unchanged | Only sunglasses are added; outline style and background colour stay put |
✅ Put the character from image 1 into the scene from image 2, keep image 2's style | Fusion that retains characteristics of both inputs |
⚠️ Make it look better | Too vague — the scope of change becomes unpredictable |
image[] upload order.Response Format
{
"created": 0,
"data": [
{
"url": "https://apac.ossforai.com/2026/08/12/09b026d5-3492-4678-907c-e25972e6c914.jpg"
}
],
"usage": {
"prompt_tokens": 1000,
"total_tokens": 1000
}
}
- Each
data[]entry contains eitherurlorb64_jsondepending onresponse_format— never both. revised_promptis not returned — do not assume it exists.b64_jsonis raw base64 with nodata:image/...;base64,prefix — decode it directly.createdis always0and cannot be used as a timestamp.- Output dimensions are determined by the input image, so do not predict width/height from request parameters.
usage cannot be used for reconciliation: prompt_tokens is always 1000 x n, a placeholder. Editing costs the same as text-to-image at a flat rate per image; use the APIYI Console billing records for actual charges.Authorizations
API Key created in the APIYI Console
Body
Model ID
grok-imagine-image, grok-imagine-image-quality Editing instruction. State what to change and explicitly ask for everything else to stay put,
e.g. Change the scarf color to bright RED. Keep everything else exactly the same.
"Change the scarf color to bright RED. Keep everything else exactly the same."
Reference image file. For multi-image fusion repeat the image[] field (1-3 files);
upload order is what "image 1 / image 2 / image 3" refers to in the prompt.
Accepted formats: png / jpg / webp.
Number of output images, 1-10. Independent of the number of reference images
1 <= x <= 101
Response format. url returns a direct link; b64_json returns raw base64 (no data: prefix)
url, b64_json "url"
Response
Image generated successfully
Creation timestamp. Always 0 for this model — do not use it for timing
0
Array of image results, length equals the requested n
Show child attributes
Show child attributes
Placeholder values — do not use for billing reconciliation. prompt_tokens is always 1000 x n
Show child attributes
Show child attributes
Was this page helpful?