Skip to content

Guide: Image Generation with the SDK

Generate images from text prompts using 25+ AI models through the official FOTOhub SDKs.

Getting Started

Image generation is synchronous — you get results immediately (1-15 seconds depending on model). For batch workflows processing 10+ images, see the Batch Processing Guide.

Prerequisites


Basic Generation

python
from fotohub import FotoHub

client = FotoHub()

result = client.generate_image(
    prompt="A serene Japanese garden with cherry blossoms, golden hour lighting",
    model="seedream-5-0-260128",
)

print(result.images[0].url)
typescript
import { FotoHub } from "fotohub";

const client = new FotoHub({ apiKey: process.env.FOTOHUB_API_KEY! });

const result = await client.generateImage({
  prompt: "A serene Japanese garden with cherry blossoms, golden hour lighting",
  model: "seedream-5-0-260128",
});

console.log(result.images[0].url);
go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
)

func main() {
    payload, _ := json.Marshal(map[string]interface{}{
        "prompt": "A serene Japanese garden with cherry blossoms, golden hour lighting",
        "model":  "seedream-5-0-260128",
    })

    req, _ := http.NewRequest("POST",
        "https://apis.fotohub.app/v1/ai/generate/image",
        bytes.NewBuffer(payload))
    req.Header.Set("Authorization", "Bearer "+os.Getenv("FOTOHUB_API_KEY"))
    req.Header.Set("Content-Type", "application/json")

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    var result map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&result)
    images := result["images"].([]interface{})
    url := images[0].(string)
    fmt.Println(url)
}
bash
curl -X POST https://apis.fotohub.app/v1/ai/generate/image \
  -H "Authorization: Bearer $FOTOHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "A serene Japanese garden with cherry blossoms, golden hour lighting",
    "model": "seedream-5-0-260128"
  }'

Parameters

ParameterTypeDefaultDescription
promptstringrequiredText description of the image
modelstringseedream-5-0-260128AI model ID
negative_promptstringWhat to avoid in the image
aspect_ratiostring1:11:1, 16:9, 9:16, 4:3, 3:4
num_imagesint1Number of images (1-4)
guidance_scalefloatvariesPrompt adherence (higher = more literal)
seedintrandomReproducible results
image_urlstringReference image for editing/i2i
image_urlsstring[]Multiple reference images (Grok Imagine Pro)

Choose a Model

ModelBest forSpeedCredits/image
seedream-5-0-260128General purpose, fast2-4s1.0
flux-2-proPhotorealism, text rendering5-8s1.5
grok-imagine-imageFast budget generation (1K)2-3s1.0
grok-imagine-image-proMulti-image, virtual try-on (2K)8-12s3.0
flux-2-maxIllustration, anime, stylized art6-10s3.0
imagen-4-standardHigh detail, complex scenes6-10s3.0
imagen-4-ultraMaximum quality, fine detail10-15s5.0
gemini-3-pro-imageComplex/precise prompts, in-image text rendering (up to 4K)8-14s6.0

See the full Models Catalog for all available models.


Prompt Engineering

The quality of your prompt directly determines output quality. Follow these principles:

Structure Your Prompts

Use this template for consistent results:

[Subject] + [Setting/Environment] + [Style/Medium] + [Lighting] + [Camera/Composition]

Examples:

Template PartGoodAvoid
Subject"A tabby cat sitting on a velvet cushion""A cat"
Setting"in a Victorian library with floor-to-ceiling bookshelves""in a room"
Style"oil painting style, impasto brushstrokes""nice looking"
Lighting"warm candlelight, golden hour shadows""good lighting"
Composition"close-up portrait, shallow depth of field"(omitting it)

Prompt Length Sweet Spot

Prompt lengthResult qualityUse case
5-15 wordsBasic, often genericQuick tests
30-60 wordsOptimal detail balanceProduction use
60-120 wordsVery specific, may over-constrainComplex scenes
120+ wordsDiminishing returns, model may ignore partsAvoid

Power Words by Category

Photorealism:photorealistic, 8K, raw photo, DSLR, Canon EOS R5, 85mm lens, bokeh, natural lighting, subsurface scattering

Illustration:digital art, concept art, matte painting, trending on ArtStation, highly detailed, intricate

Cinematic:cinematic still, anamorphic lens, film grain, color grading, dramatic lighting, wide angle

Product:product photography, studio lighting, white background, soft shadows, commercial, catalog shot


Style Consistency Across Images

When generating multiple images for a project (e.g., a brand campaign or storyboard), maintain visual consistency:

Method 1: Seed Locking

python
from fotohub import FotoHub

client = FotoHub()

# Base style seed — all variations will share visual DNA
BASE_SEED = 42857

scenes = [
    "Hero arriving at the castle gates",
    "Hero exploring the throne room",
    "Hero confronting the dragon",
]

results = []
for scene in scenes:
    result = client.generate_image(
        prompt=f"Fantasy illustration, cel-shaded style, vibrant colors. Scene: {scene}",
        model="seedream-5-0-260128",
        seed=BASE_SEED,
        guidance_scale=7.5,
    )
    results.append(result)
typescript
import { FotoHub } from "fotohub";

const client = new FotoHub({ apiKey: process.env.FOTOHUB_API_KEY! });

const BASE_SEED = 42857;

const scenes = [
  "Hero arriving at the castle gates",
  "Hero exploring the throne room",
  "Hero confronting the dragon",
];

const results = await Promise.all(
  scenes.map((scene) =>
    client.generateImage({
      prompt: `Fantasy illustration, cel-shaded style, vibrant colors. Scene: ${scene}`,
      model: "seedream-5-0-260128",
      seed: BASE_SEED,
      guidanceScale: 7.5,
    })
  )
);
go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
)

func main() {
    baseSeed := 42857
    scenes := []string{
        "Hero arriving at the castle gates",
        "Hero exploring the throne room",
        "Hero confronting the dragon",
    }

    for _, scene := range scenes {
        payload, _ := json.Marshal(map[string]interface{}{
            "prompt":         fmt.Sprintf("Fantasy illustration, cel-shaded style, vibrant colors. Scene: %s", scene),
            "model":          "seedream-5-0-260128",
            "seed":           baseSeed,
            "guidance_scale": 7.5,
        })

        req, _ := http.NewRequest("POST",
            "https://apis.fotohub.app/v1/ai/generate/image",
            bytes.NewBuffer(payload))
        req.Header.Set("Authorization", "Bearer "+os.Getenv("FOTOHUB_API_KEY"))
        req.Header.Set("Content-Type", "application/json")

        resp, _ := http.DefaultClient.Do(req)
        resp.Body.Close()
        fmt.Printf("Generated: %s\n", scene)
    }
}
bash
# Generate consistent style across multiple scenes
for SCENE in "Hero arriving at the castle gates" "Hero exploring the throne room" "Hero confronting the dragon"; do
  curl -s -X POST https://apis.fotohub.app/v1/ai/generate/image \
    -H "Authorization: Bearer $FOTOHUB_API_KEY" \
    -H "Content-Type: application/json" \
    -d "{
      \"prompt\": \"Fantasy illustration, cel-shaded style, vibrant colors. Scene: $SCENE\",
      \"model\": \"seedream-5-0-260128\",
      \"seed\": 42857,
      \"guidance_scale\": 7.5
    }" | jq -r '.images[0].url'
done

Method 2: Style Prefix Template

Create a reusable style prefix and prepend to all prompts:

python
# Define your brand style once
BRAND_STYLE = (
    "Minimalist flat illustration, muted pastel palette (dusty rose, sage green, cream), "
    "clean geometric shapes, subtle paper texture, no outlines, modern editorial style. "
)

# Apply to all generations
scenes = {
    "hero": "Person working at a standing desk with a laptop",
    "features": "Three floating cards showing app features",
    "testimonial": "Happy customer holding a phone with the app open",
}

for name, scene in scenes.items():
    result = client.generate_image(
        prompt=BRAND_STYLE + scene,
        model="seedream-5-0-260128",
        aspect_ratio="16:9",
    )
    print(f"{name}: {result.images[0].url}")

Method 3: Reference Image (Grok Imagine Pro)

Use a reference image to maintain exact style:

python
# Generate first image to establish style
reference = client.generate_image(
    prompt="Minimalist product packaging, pastel colors, clean design",
    model="seedream-5-0-260128",
)

# Use it as reference for subsequent images
for product in ["headphones", "watch", "phone case"]:
    result = client.generate_image(
        prompt=f"Same minimalist packaging style, {product} product box",
        model="grok-imagine-image-pro",
        image_url=reference.images[0].url,  # Style reference
    )

Aspect Ratios and Resolution

Available Aspect Ratios

RatioPixels (approx)Use case
1:11024x1024Avatars, thumbnails, social posts
16:91365x768Desktop wallpapers, YouTube thumbnails
9:16768x1365Stories, Reels, TikTok, mobile
4:31182x886Presentations, traditional photos
3:4886x1182Portraits, book covers, Pinterest

Resolution Tips

python
# Portrait for Instagram Story
result = client.generate_image(
    prompt="Fashion model in studio, full body shot",
    model="seedream-5-0-260128",
    aspect_ratio="9:16",
)

# Landscape for website hero
result = client.generate_image(
    prompt="Mountain panorama at golden hour, dramatic clouds",
    model="seedream-5-0-260128",
    aspect_ratio="16:9",
)

# Square for product thumbnail
result = client.generate_image(
    prompt="Minimalist watch on marble surface, top-down",
    model="seedream-5-0-260128",
    aspect_ratio="1:1",
)
typescript
// Portrait for Instagram Story
const story = await client.generateImage({
  prompt: "Fashion model in studio, full body shot",
  model: "seedream-5-0-260128",
  aspectRatio: "9:16",
});

// Landscape for website hero
const hero = await client.generateImage({
  prompt: "Mountain panorama at golden hour, dramatic clouds",
  model: "seedream-5-0-260128",
  aspectRatio: "16:9",
});

// Square for product thumbnail
const thumb = await client.generateImage({
  prompt: "Minimalist watch on marble surface, top-down",
  model: "seedream-5-0-260128",
  aspectRatio: "1:1",
});
go
package main

import (
    "bytes"
    "encoding/json"
    "net/http"
    "os"
)

func generateWithAspect(prompt, aspect string) {
    payload, _ := json.Marshal(map[string]string{
        "prompt":       prompt,
        "model":        "seedream-5-0-260128",
        "aspect_ratio": aspect,
    })
    req, _ := http.NewRequest("POST",
        "https://apis.fotohub.app/v1/ai/generate/image",
        bytes.NewBuffer(payload))
    req.Header.Set("Authorization", "Bearer "+os.Getenv("FOTOHUB_API_KEY"))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}

func main() {
    generateWithAspect("Fashion model in studio", "9:16")   // Story
    generateWithAspect("Mountain panorama at golden hour", "16:9") // Hero
    generateWithAspect("Minimalist watch, top-down", "1:1")  // Thumbnail
}
bash
# Portrait for Stories
curl -X POST https://apis.fotohub.app/v1/ai/generate/image \
  -H "Authorization: Bearer $FOTOHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Fashion model in studio", "model": "seedream-5-0-260128", "aspect_ratio": "9:16"}'

# Landscape for hero
curl -X POST https://apis.fotohub.app/v1/ai/generate/image \
  -H "Authorization: Bearer $FOTOHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Mountain panorama", "model": "seedream-5-0-260128", "aspect_ratio": "16:9"}'

Batch Generation

Generate multiple variations in one call:

python
result = client.generate_image(
    prompt="A futuristic city at night",
    model="seedream-5-0-260128",
    num_images=4,
    seed=42,  # reproducible base
)

for i, image in enumerate(result.images):
    print(f"Image {i+1}: {image.url}")
typescript
const result = await client.generateImage({
  prompt: "A futuristic city at night",
  model: "seedream-5-0-260128",
  numImages: 4,
  seed: 42,
});

result.images.forEach((img, i) => {
  console.log(`Image ${i + 1}: ${img.url}`);
});
go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
)

func main() {
    payload, _ := json.Marshal(map[string]interface{}{
        "prompt":     "A futuristic city at night",
        "model":      "seedream-5-0-260128",
        "num_images": 4,
        "seed":       42,
    })
    req, _ := http.NewRequest("POST",
        "https://apis.fotohub.app/v1/ai/generate/image",
        bytes.NewBuffer(payload))
    req.Header.Set("Authorization", "Bearer "+os.Getenv("FOTOHUB_API_KEY"))
    req.Header.Set("Content-Type", "application/json")
    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()

    var result map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&result)
    images := result["images"].([]interface{})
    for i, img := range images {
        url := img.(string)
        fmt.Printf("Image %d: %s\n", i+1, url)
    }
}
bash
curl -X POST https://apis.fotohub.app/v1/ai/generate/image \
  -H "Authorization: Bearer $FOTOHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "A futuristic city at night",
    "model": "seedream-5-0-260128",
    "num_images": 4,
    "seed": 42
  }' | jq '.images[].url'

Negative Prompts

Tell the model what to avoid. Negative prompts work best with models that have a guidance_scale parameter:

python
result = client.generate_image(
    prompt="Professional headshot, natural lighting, warm tones, sharp focus",
    negative_prompt="blurry, distorted, cartoon, anime, low quality, watermark, "
                    "oversaturated, plastic skin, bad anatomy, extra fingers",
    model="seedream-5-0-260128",
    guidance_scale=8.0,
)

Common Negative Prompt Templates

Use caseNegative prompt
Photorealismcartoon, anime, drawing, painting, illustration, 3d render, CGI
Product photosblurry, low quality, watermark, text, busy background, shadows
Portraitsbad anatomy, extra fingers, mutated hands, deformed face, cross-eyed
Landscapespeople, text, watermark, frame, border, signature

Guidance Scale Tuning

The guidance_scale parameter controls how strictly the model follows your prompt:

ValueEffectBest for
1-3Very creative, loose interpretationAbstract art, surprises
5-7Balanced (default for most models)General use
8-12Strict prompt adherenceTechnical accuracy, specific requests
12-20Over-constrained, may look unnaturalAvoid for most cases
python
# Creative/abstract (low guidance)
abstract = client.generate_image(
    prompt="Emotional landscape of solitude",
    guidance_scale=3.0,
)

# Precise technical (high guidance)
technical = client.generate_image(
    prompt="Isometric 3D icon of a blue folder, flat shading, white background",
    guidance_scale=10.0,
)

Reproducible Results

Use seed for deterministic output:

python
# Same seed + same prompt + same model = same image
result1 = client.generate_image(prompt="A red rose", seed=12345, model="seedream-5-0-260128")
result2 = client.generate_image(prompt="A red rose", seed=12345, model="seedream-5-0-260128")
# result1.images[0].url == result2.images[0].url

Seed Exploration

Generate with num_images=4 (no seed) to explore variations, then lock the seed of your favorite for consistent output.


xAI Grok Imagine — Image Editing & Multi-Image

Grok Imagine Pro supports editing with up to 3 reference images — perfect for product composites, virtual try-on, and architectural visualization.

Single Image Edit

python
# Edit a product photo — change context/background
result = client.generate_image(
    prompt="Product displayed in a modern minimalist kitchen, marble countertop, natural light",
    model="grok-imagine-image-pro",
    image_url="https://example.com/my-product.jpg",
    aspect_ratio="3:4",
)
typescript
const result = await client.generateImage({
  prompt: "Product displayed in a modern minimalist kitchen, marble countertop, natural light",
  model: "grok-imagine-image-pro",
  imageUrl: "https://example.com/my-product.jpg",
  aspectRatio: "3:4",
});
go
payload, _ := json.Marshal(map[string]interface{}{
    "prompt":       "Product displayed in a modern minimalist kitchen, marble countertop, natural light",
    "model":        "grok-imagine-image-pro",
    "image_url":    "https://example.com/my-product.jpg",
    "aspect_ratio": "3:4",
})
// ... standard request setup
bash
curl -X POST https://apis.fotohub.app/v1/ai/generate/image \
  -H "Authorization: Bearer $FOTOHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Product displayed in a modern minimalist kitchen, marble countertop, natural light",
    "model": "grok-imagine-image-pro",
    "image_url": "https://example.com/my-product.jpg",
    "aspect_ratio": "3:4"
  }'

Multi-Image Combine (up to 3 references)

python
# Combine multiple product images into one listing
result = client.generate_image(
    prompt="All products arranged in elegant flat-lay, white background, e-commerce style",
    model="grok-imagine-image-pro",
    image_urls=[
        "https://example.com/product-1.jpg",
        "https://example.com/product-2.jpg",
        "https://example.com/product-3.jpg",
    ],
    aspect_ratio="1:1",
)

Virtual Try-On (Person + Garment)

python
# Show a person wearing a specific garment
result = client.generate_image(
    prompt="Virtual try-on: person wearing the garment, natural fit, photorealistic",
    model="grok-imagine-image-pro",
    image_urls=[
        "https://example.com/model-photo.jpg",   # Person (first)
        "https://example.com/dress.jpg",          # Garment (second)
    ],
    aspect_ratio="3:4",
)

WARNING

  • Multi-image (2-3 refs) requires grok-imagine-image-pro — the basic model only supports single-image edit
  • All image URLs must be publicly accessible
  • For virtual try-on: person photo first, then garment/product

Download & Save

python
import httpx
from pathlib import Path

result = client.generate_image(prompt="Mountain landscape", model="seedream-5-0-260128")
url = result.images[0].url

# Download the image
response = httpx.get(url)
Path("output.png").write_bytes(response.content)
print(f"Saved: output.png ({len(response.content)} bytes)")
typescript
import { writeFile } from "fs/promises";

const result = await client.generateImage({
  prompt: "Mountain landscape",
  model: "seedream-5-0-260128",
});
const url = result.images[0].url;

const response = await fetch(url);
const buffer = Buffer.from(await response.arrayBuffer());
await writeFile("output.png", buffer);
console.log(`Saved: output.png (${buffer.length} bytes)`);
go
package main

import (
    "io"
    "net/http"
    "os"
)

func downloadImage(url, filename string) error {
    resp, err := http.Get(url)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    file, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer file.Close()

    _, err = io.Copy(file, resp.Body)
    return err
}
bash
# Generate and download in one pipeline
URL=$(curl -s -X POST https://apis.fotohub.app/v1/ai/generate/image \
  -H "Authorization: Bearer $FOTOHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Mountain landscape", "model": "seedream-5-0-260128"}' \
  | jq -r '.images[0].url')

curl -o output.png "$URL"
echo "Downloaded: output.png"

Error Handling

python
from fotohub import FotoHub, ValidationError, InsufficientCreditsError, RateLimitError
import time

client = FotoHub()

try:
    result = client.generate_image(prompt="test", model="seedream-5-0-260128")
except ValidationError as e:
    print(f"Invalid params: {e.message}")
except InsufficientCreditsError:
    print("Not enough credits — top up at fotohub.app/billing")
except RateLimitError as e:
    print(f"Rate limited — retry after {e.retry_after}s")
    time.sleep(e.retry_after)
typescript
import { ValidationError, InsufficientCreditsError, RateLimitError } from "fotohub/errors";

try {
  const result = await client.generateImage({ prompt: "test", model: "seedream-5-0-260128" });
} catch (e) {
  if (e instanceof ValidationError) {
    console.error(`Invalid: ${e.message}`);
  } else if (e instanceof InsufficientCreditsError) {
    console.error("Top up at fotohub.app/billing");
  } else if (e instanceof RateLimitError) {
    console.error(`Rate limited: retry in ${e.retryAfter}s`);
  }
}
go
// Check HTTP status code
if resp.StatusCode == 429 {
    // Rate limited — check Retry-After header
    retryAfter := resp.Header.Get("Retry-After")
    fmt.Printf("Rate limited. Retry after %s seconds\n", retryAfter)
} else if resp.StatusCode == 402 {
    fmt.Println("Insufficient credits")
} else if resp.StatusCode >= 400 {
    fmt.Printf("Error: HTTP %d\n", resp.StatusCode)
}
bash
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
  https://apis.fotohub.app/v1/ai/generate/image \
  -H "Authorization: Bearer $FOTOHUB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "test", "model": "seedream-5-0-260128"}')

HTTP_CODE=$(echo "$RESPONSE" | tail -1)
if [ "$HTTP_CODE" -ge 400 ]; then
  echo "Error ($HTTP_CODE): $(echo "$RESPONSE" | sed '$d' | jq -r '.error.message')"
fi

See the full Error Handling Guide for retry patterns and circuit breakers.


Performance Tips

  1. Use the fastest model for prototyping — seedream and grok-imagine-image are 2-4s
  2. Batch with num_images — Generating 4 images in one call is faster than 4 separate calls
  3. Cache with seeds — If you regenerate the same image often, lock the seed and cache the URL
  4. Lower guidance for speed — Some models are faster at lower guidance_scale values
  5. Choose appropriate resolution — Don't generate 2K images for thumbnails
  6. Use negative prompts sparingly — Long negative prompts can slow generation

Model Comparison: When to Use What

ScenarioRecommended modelWhy
Landing page heroseedream-5-0-260128Fast, versatile, great default
Product photo with textflux-2-proBest text rendering in images
E-commerce flat-laygrok-imagine-image-proMulti-image compose
Character designflux-2-maxBest for illustration/anime
Architecture visualizationimagen-4-standardHandles complex spatial scenes
Social media batch (50+)grok-imagine-imageCheapest per image at scale
Print-quality artworkimagen-4-ultraHighest resolution & detail
Complex/precise prompts, in-image textgemini-3-pro-imageAdvanced reasoning + precise text rendering, up to 4K