curl --request POST \
--url https://apis.viggle.ai/v1/images \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form 'prompt=<string>' \
--form model=qwen \
--form aspect_ratio=1:1 \
--form resolution=1K \
--form n=1 \
--form seed=1073741823 \
--form reference_image=@example-file \
--form 'reference_image_url=<string>'import requests
url = "https://apis.viggle.ai/v1/images"
files = { "reference_image": ("example-file", open("example-file", "rb")) }
payload = {
"prompt": "<string>",
"model": "qwen",
"aspect_ratio": "1:1",
"resolution": "1K",
"n": "1",
"seed": "1073741823",
"reference_image_url": "<string>"
}
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('prompt', '<string>');
form.append('model', 'qwen');
form.append('aspect_ratio', '1:1');
form.append('resolution', '1K');
form.append('n', '1');
form.append('seed', '1073741823');
form.append('reference_image', '<string>');
form.append('reference_image_url', '<string>');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://apis.viggle.ai/v1/images', 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://apis.viggle.ai/v1/images",
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=\"prompt\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\nqwen\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n1:1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n1K\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"n\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1073741823\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\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://apis.viggle.ai/v1/images"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\nqwen\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n1:1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n1K\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"n\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1073741823\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\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://apis.viggle.ai/v1/images")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\nqwen\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n1:1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n1K\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"n\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1073741823\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://apis.viggle.ai/v1/images")
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=\"prompt\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\nqwen\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n1:1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n1K\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"n\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1073741823\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"status": "queued",
"model": "<string>",
"progress": 50,
"images": [
{
"url": "<string>",
"width": 2,
"height": 2,
"seed": 1
}
],
"created_at": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z",
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}Generate Image
Generate 1–6 PNG images from a text prompt, optionally guided by up to 8 reference images.
curl --request POST \
--url https://apis.viggle.ai/v1/images \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form 'prompt=<string>' \
--form model=qwen \
--form aspect_ratio=1:1 \
--form resolution=1K \
--form n=1 \
--form seed=1073741823 \
--form reference_image=@example-file \
--form 'reference_image_url=<string>'import requests
url = "https://apis.viggle.ai/v1/images"
files = { "reference_image": ("example-file", open("example-file", "rb")) }
payload = {
"prompt": "<string>",
"model": "qwen",
"aspect_ratio": "1:1",
"resolution": "1K",
"n": "1",
"seed": "1073741823",
"reference_image_url": "<string>"
}
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('prompt', '<string>');
form.append('model', 'qwen');
form.append('aspect_ratio', '1:1');
form.append('resolution', '1K');
form.append('n', '1');
form.append('seed', '1073741823');
form.append('reference_image', '<string>');
form.append('reference_image_url', '<string>');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://apis.viggle.ai/v1/images', 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://apis.viggle.ai/v1/images",
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=\"prompt\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\nqwen\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n1:1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n1K\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"n\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1073741823\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\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://apis.viggle.ai/v1/images"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\nqwen\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n1:1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n1K\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"n\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1073741823\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\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://apis.viggle.ai/v1/images")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\nqwen\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n1:1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n1K\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"n\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1073741823\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://apis.viggle.ai/v1/images")
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=\"prompt\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\nqwen\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n1:1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n1K\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"n\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1073741823\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"status": "queued",
"model": "<string>",
"progress": 50,
"images": [
{
"url": "<string>",
"width": 2,
"height": 2,
"seed": 1
}
],
"created_at": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z",
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}multipart/form-data with a prompt and, optionally, reference images. The request is asynchronous: it answers immediately with a queued Image object whose ID starts with img_, and you poll Get Image until status is ready or failed. Without reference images this is text-to-image; with at least one, the model is guided by them.
1K, 2 credits ($0.02) per image at 2K. A job costs n × the per-image price — for example, 2K with n=3 is 6 credits. The whole amount is reserved when the job is created and refunded in full if it fails. aspect_ratio, seed, and reference images don’t affect price. See Pricing and retention.Request parameters
Sendmultipart/form-data.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
prompt | string | Yes | — | Text description of the images. Up to 4000 characters, counted as Unicode code points (one CJK character counts as one). Must not be blank. |
model | string | No | qwen | Image model. Only qwen is available. |
aspect_ratio | string | No | 1:1 | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, or 2:3. Together with resolution, selects the exact output size — see Output sizes. |
resolution | string | No | 1K | 1K or 2K, case-insensitive. Sets the output size tier and the per-image price: 1 credit at 1K, 2 credits at 2K. |
n | integer | No | 1 | Number of images to generate, 1–6. |
seed | integer | No | random | 0–2147483647. Image i (counting from 0) uses (seed + i) mod 2147483648. Omitted, the server picks a random seed. Every image reports the seed it actually used. |
reference_image | file, repeatable | No | — | Reference image upload, up to 24 MiB each. Repeat the field to send more than one. |
reference_image_url | string, repeatable | No | — | Publicly reachable http(s) image URL, under the same limits as reference_image. Repeat the field to send more than one. |
reference_image and reference_image_url. The model receives the uploaded files first, then the URLs, each in the order you sent them. Supplying any reference image moves 2K to a smaller size — see the next section.
Reference image limits
- Up to 24 MiB each, with a long-to-short side ratio of at most 8.
- JPEG, PNG, WebP, GIF, BMP, TIFF, or HEIC.
- An image with a long side of 3840 px or more, or a short side of 2160 px or more, is first downscaled, keeping its aspect ratio, to fit within 1920 × 1080 (1080 × 1920 for a portrait image).
- A
reference_image_urlmust be publicly reachable; it is downloaded while the create request is being processed.
Output sizes
aspect_ratio and resolution together select one of the model’s fixed sizes — you get exactly the size listed. Width × height, in pixels:
aspect_ratio | 1K | 2K (text only) | 2K (with reference images) |
|---|---|---|---|
1:1 | 1024 × 1024 | 2048 × 2048 | 1536 × 1536 |
16:9 | 1376 × 768 | 2720 × 1536 | 2048 × 1152 |
9:16 | 768 × 1376 | 1536 × 2720 | 1152 × 2048 |
4:3 | 1184 × 896 | 2368 × 1760 | 1760 × 1344 |
3:4 | 896 × 1184 | 1760 × 2368 | 1344 × 1760 |
3:2 | 1248 × 832 | 2496 × 1664 | 1888 × 1248 |
2:3 | 832 × 1248 | 1664 × 2496 | 1248 × 1888 |
1K is the same with or without reference images. 2K is about 2048² pixels of area for text-to-image and about 1536² with reference images; the price is the same either way. Each image’s width and height in the response are its actual size.
Response parameters
Returns200 OK with an Image object in queued state — the same shape Get Image returns. progress is 0, images is empty, and completed_at and error are null.
{
"id": "img_b16a190d-2d44-41a0-8056-ef68183c3b9e",
"status": "queued",
"model": "qwen",
"progress": 0,
"images": [],
"created_at": "2026-10-12T08:20:00Z",
"completed_at": null,
"error": null
}
Common errors
| Error | Cause |
|---|---|
400 INVALID_REQUEST | A parameter is invalid — message names which one — or the body isn’t multipart/form-data. No job is created and no file is uploaded. |
401 UNAUTHENTICATED | Missing or invalid API key. |
402 INSUFFICIENT_CREDITS | The balance can’t cover n × the per-image price; message gives the credits required. No generation starts. |
429 RATE_LIMITED | Too many concurrent create requests on the same key. Retry later. |
500 INTERNAL_ERROR | Internal server error. Retryable. |
503 SERVICE_BUSY | Service temporarily unavailable. Retryable. |
{"error": {"code", "message", "retryable", "request_id", "details", "remediation"}}. See Errors and recovery. A job that is accepted and later fails reports its failure in the Image object’s error field instead — see Get Image.
User-Agent header. Python urllib’s default User-Agent has been observed to be blocked in front of apis.viggle.ai, which answers with a non-JSON error page instead of an API error.The body must be multipart/form-data, even without reference images — an application/x-www-form-urlencoded body gets 400. With Python requests, pass the fields through files= (as below), not data= alone.Examples
curl -X POST https://apis.viggle.ai/v1/images \
-H "Authorization: Bearer $VIGGLE_API_KEY" \
-F prompt="A watercolor fox reading a book under a maple tree" \
-F aspect_ratio=16:9 \
-F resolution=1K \
-F n=2
import os, requests
fields = {"prompt": "A watercolor fox reading a book under a maple tree", "aspect_ratio": "16:9", "resolution": "1K", "n": "2"}
response = requests.post(
"https://apis.viggle.ai/v1/images",
headers={"Authorization": f"Bearer {os.environ['VIGGLE_API_KEY']}"},
files={name: (None, value) for name, value in fields.items()}, # forces multipart/form-data
)
response.raise_for_status()
image = response.json()
const form = new FormData();
form.append("prompt", "A watercolor fox reading a book under a maple tree");
form.append("aspect_ratio", "16:9");
form.append("resolution", "1K");
form.append("n", "2");
const response = await fetch("https://apis.viggle.ai/v1/images", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.VIGGLE_API_KEY}` },
body: form,
});
const image = await response.json();
body := &bytes.Buffer{}; w := multipart.NewWriter(body)
w.WriteField("prompt", "A watercolor fox reading a book under a maple tree"); w.WriteField("aspect_ratio", "16:9"); w.WriteField("resolution", "1K"); w.WriteField("n", "2"); w.Close()
req, _ := http.NewRequest("POST", "https://apis.viggle.ai/v1/images", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("VIGGLE_API_KEY")); req.Header.Set("Content-Type", w.FormDataContentType())
resp, err := http.DefaultClient.Do(req); if err != nil { panic(err) }; defer resp.Body.Close()
With reference images
Uploads and URLs can be mixed, and both fields can be repeated:curl -X POST https://apis.viggle.ai/v1/images \
-H "Authorization: Bearer $VIGGLE_API_KEY" \
-F prompt="The same fox, now in winter, snow falling" \
-F aspect_ratio=1:1 \
-F resolution=2K \
-F [email protected] \
-F reference_image_url=https://example.com/style.jpg
import os, requests
with open("fox.png", "rb") as fox:
response = requests.post(
"https://apis.viggle.ai/v1/images",
headers={"Authorization": f"Bearer {os.environ['VIGGLE_API_KEY']}"},
data={
"prompt": "The same fox, now in winter, snow falling",
"aspect_ratio": "1:1",
"resolution": "2K",
"reference_image_url": ["https://example.com/style.jpg"],
},
files=[("reference_image", ("fox.png", fox, "image/png"))],
)
response.raise_for_status()
image = response.json()
import { openAsBlob } from "node:fs";
const form = new FormData();
form.append("prompt", "The same fox, now in winter, snow falling");
form.append("aspect_ratio", "1:1");
form.append("resolution", "2K");
form.append("reference_image", await openAsBlob("fox.png"), "fox.png");
form.append("reference_image_url", "https://example.com/style.jpg");
const response = await fetch("https://apis.viggle.ai/v1/images", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.VIGGLE_API_KEY}` },
body: form,
});
const image = await response.json();
@viggle/sdk
Requires an @viggle/sdk version that includes the images resource.
import { Viggle } from "@viggle/sdk";
const viggle = new Viggle({ apiKey: process.env.VIGGLE_API_KEY! });
const image = await viggle.images.create({
prompt: "A watercolor fox reading a book under a maple tree",
aspectRatio: "16:9",
resolution: "1K",
n: 2,
});
// image.id, image.status ("queued"); poll with viggle.images.get(image.id)
Next step
Use Get Image with the returnedid to poll for the result.
Quick Start: Viggle Image
Get Image
Authorizations
Server-side SDK clients use a project API key. Remote MCP clients use an OAuth access token. Never expose a project API key in browser code.
Headers
Optional caller-supplied correlation ID, up to 128 characters. The service returns the effective value in the response header for tracing and support.
1 - 128Optional source-channel label, up to 128 characters, used to attribute traffic to an SDK, integration, product surface, or internal workflow.
1 - 128Body
Body of POST /v1/images, multipart/form-data only. Text-to-image
when no reference image is supplied, reference-guided generation when
at least one is. aspect_ratio and resolution select one of the
model's fixed output sizes (width × height, in pixels); with
reference images, 2K is a smaller size than without:
| aspect_ratio | 1K | 2K | 2K with reference images |
|---|---|---|---|
| 1:1 | 1024×1024 | 2048×2048 | 1536×1536 |
| 16:9 | 1376×768 | 2720×1536 | 2048×1152 |
| 9:16 | 768×1376 | 1536×2720 | 1152×2048 |
| 4:3 | 1184×896 | 2368×1760 | 1760×1344 |
| 3:4 | 896×1184 | 1760×2368 | 1344×1760 |
| 3:2 | 1248×832 | 2496×1664 | 1888×1248 |
| 2:3 | 832×1248 | 1664×2496 | 1248×1888 |
Up to 8 reference images in total, as any mix of reference_image
uploads and reference_image_url URLs. The model receives the
uploads first, then the URLs, each in the order sent.
Text description of the images. Up to 4000 characters, counted as Unicode code points (one CJK character counts as one). Must not be blank.
1 - 4000Image model. Only qwen is available.
qwen Output aspect ratio. Together with resolution, it selects the exact output size.
1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 Output size tier, case-insensitive, and the price of each image — 1 credit at 1K, 2 credits at 2K. With any reference image, 2K is about 1536² pixels instead of about 2048².
1K, 2K Number of images to generate. The job reserves n × the per-image price.
1 <= x <= 6Image i (counting from 0) is generated with seed
(seed + i) mod 2147483648. Omitted, the server picks a random
seed; each image reports the seed it used.
0 <= x <= 2147483647Reference image upload; repeat the field for more than one. Each up to 24 MiB, with a long-to-short side ratio of at most 8; JPEG, PNG, WebP, GIF, BMP, TIFF, or HEIC. Counts toward the 8-image total shared with reference_image_url.
8Publicly reachable http(s) reference image URL, downloaded while the create request is processed; repeat the field for more than one. Same limits as reference_image, and counts toward the same 8-image total.
8Response
Image generation accepted and queued.
An image generation job (img_ prefix), returned by both
POST /v1/images and GET /v1/images/{image_id}. It only moves
forward — queued → processing → ready or failed — and is
never cancelled. images stays empty until the job is ready,
then lists every generated image in request order. A job either
produces all n images or fails; it never returns a partial set.
Public image job ID, img_ prefix.
1Lifecycle state. ready and failed are terminal.
queued, processing, ready, failed Model used for generation; currently always qwen.
Share of the requested images generated so far — 0 while queued, 0–99 while processing, 100 when ready, null when failed.
0 <= x <= 100Empty until ready, then all n generated images in request order.
Show child attributes
Show child attributes
Creation timestamp, RFC 3339.
Terminal-state timestamp; set once the job is ready or failed, null before that.
Failure details, only when status is failed; otherwise null.
code is SERVICE_BUSY (retryable — the generation service is
busy or unavailable, or the job did not finish within 15
minutes), TASK_FAILED (retryable — any other generation or
upload failure), or INVALID_REQUEST (not retryable — the model
rejected the request, for example an unusable reference image).
Show child attributes
Show child attributes

