Skip to main content

Overview

The NanoGPT API provides image generation through the OpenAI-compatible endpoint. This guide covers text-to-image and image-to-image flows, response formats, and best practices.

OpenAI-Compatible Endpoint (v1/images/generations)

This endpoint follows the OpenAI specification for image generation and returns a list of base64-encoded images by default (or hosted URLs when response_format: "url").

Basic Image Generation

curl https://nano-gpt.com/v1/images/generations \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "hidream",
    "prompt": "A sunset over a mountain range",
    "n": 1,
    "size": "1024x1024"
  }'

Return Hosted URLs (response_format: “url”)

By default, responses include base64 strings. Ask for hosted URLs instead:
{
  "response_format": "url"
}
Note on response_format: “url” The returned data[i].url is a time-limited, signed download URL (presigned URL) to the generated image. It will expire after a short period (currently ~1 hour). If you need long-term access, download the image and store it yourself.
  • Output: data will usually contain { url: "https://..." } entries instead of b64_json (each data[i] is one or the other, never both).
  • Fallback: if URL generation (upload/presign) fails, the API may return b64_json even when you requested response_format: "url".
  • Inputs: imageDataUrl accepts data URLs; download and convert remote images before sending.
  • Retention: the returned data[i].url is a signed, expiring URL (~1 hour). Generated files are kept for 24 hours, then permanently deleted. Download and store elsewhere if you need longer retention.
Example response (response_format: “url”)
{
  "created": 123,
  "data": [
    { "url": "https://...signed-url..." }
  ],
  "cost": 123,
  "paymentSource": "<string>",
  "remainingBalance": 123
}
Example response (response_format: “b64_json”)
{
  "created": 123,
  "data": [
    { "b64_json": "<base64-encoded-image-bytes>" }
  ],
  "cost": 123,
  "paymentSource": "<string>",
  "remainingBalance": 123
}

Downloading the image

If you requested response_format: “url”, download the image by performing a normal HTTP GET to data[i].url. Because the URL is signed and expiring, you should download it soon after generation.

Image-to-Image with OpenAI Endpoint

The OpenAI-compatible endpoint also supports img2img generation using the imageDataUrl parameter. Here are the different ways to provide input images:
Uploads sent via the API must be 4 MB or smaller. Compress or resize larger files before converting to a data URL.

Method 1: Upload from Local File

import requests
import base64

API_KEY = "YOUR_API_KEY"

# Read and encode your input image
with open("input_image.jpg", "rb") as image_file:
    encoded_image = base64.b64encode(image_file.read()).decode('utf-8')
    image_data_url = f"data:image/jpeg;base64,{encoded_image}"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

response = requests.post(
    "https://nano-gpt.com/v1/images/generations",
    headers=headers,
    json={
        "model": "flux-kontext",
        "prompt": "Transform this image into a watercolor painting",
        "n": 1,
        "size": "1024x1024",
        "imageDataUrl": image_data_url
    }
)

result = response.json()
# The response includes base64-encoded images in result["data"]

Method 2: Upload from Image URL

import requests
import base64
from urllib.request import urlopen

API_KEY = "YOUR_API_KEY"

# Download and encode image from URL
image_url = "https://example.com/your-image.jpg"
with urlopen(image_url) as response:
    image_data = response.read()
    encoded_image = base64.b64encode(image_data).decode('utf-8')
    # Detect image type from URL or response headers
    image_type = "jpeg"  # or detect from URL/headers
    image_data_url = f"data:image/{image_type};base64,{encoded_image}"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

response = requests.post(
    "https://nano-gpt.com/v1/images/generations",
    headers=headers,
    json={
        "model": "flux-kontext",
        "prompt": "Transform this image into a watercolor painting",
        "n": 1,
        "size": "1024x1024",
        "imageDataUrl": image_data_url
    }
)

result = response.json()

Method 3: Multiple Images (for supported models)

Some models like gpt-4o-image, flux-kontext, and gpt-image-1 support multiple input images:
import requests
import base64

API_KEY = "YOUR_API_KEY"

def image_to_data_url(image_path):
    """Convert image file to base64 data URL"""
    with open(image_path, "rb") as f:
        encoded = base64.b64encode(f.read()).decode('utf-8')
        ext = image_path.lower().split('.')[-1]
        mime_type = "jpeg" if ext == "jpg" else ext
        return f"data:image/{mime_type};base64,{encoded}"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

# Prepare multiple images
image_data_urls = [
    image_to_data_url("image1.jpg"),
    image_to_data_url("image2.png"),
    image_to_data_url("image3.jpg")
]

response = requests.post(
    "https://nano-gpt.com/v1/images/generations",
    headers=headers,
    json={
        "model": "gpt-4o-image",
        "prompt": "Combine these images into a creative collage",
        "n": 1,
        "size": "1024x1024",
        "imageDataUrls": image_data_urls  # Note: plural form for multiple images
    }
)

result = response.json()

Supported Image-to-Image Models

The following models support image input via the OpenAI endpoint: Single Image Input:
  • flux-dev-image-to-image - Image-to-image only
  • ghiblify - Transform images to Studio Ghibli style
  • gemini-flash-edit - Edit images with prompts
  • hidream-edit - Advanced image editing
  • bagel - Both text-to-image and image-to-image
  • SDXL-ArliMix-v1 - Artistic transformations
  • Upscaler - Upscale images to higher resolution
Multiple Image Input:
  • flux-kontext - Advanced context-aware generation
  • flux-kontext/dev - Development version (image-to-image only)
  • gpt-4o-image - GPT-4 powered image generation
  • gpt-image-1 - Advanced multi-image processing
Special Cases:
  • flux-lora/inpainting - Requires both imageDataUrl (base image) and maskDataUrl (mask)

Image Data URL Format

All images must be provided as base64-encoded data URLs:
data:image/[format];base64,[base64-encoded-data]
Supported formats:
  • image/jpeg or image/jpg
  • image/png
  • image/webp
  • image/gif (first frame only)

Additional Parameters for Image-to-Image

When using image-to-image models, you can include these additional parameters:
{
  "model": "flux-kontext",
  "prompt": "Transform to cyberpunk style",
  "imageDataUrl": "data:image/jpeg;base64,...",
  "size": "1024x1024",
  "n": 1,
  
  // Optional parameters (model-specific)
  "strength": 0.8,              // How much to transform (0.0-1.0)
  "guidance_scale": 7.5,        // Prompt adherence
  "num_inference_steps": 30,    // Quality vs speed
  "seed": 42,                   // For reproducible results
  "kontext_max_mode": true      // Enhanced context (flux-kontext only)
}

Best Practices

  1. Prompt Engineering
    • Be specific and detailed in your prompts
    • Include style references when needed
    • Use the negative prompt to avoid unwanted elements
    • For img2img, describe the changes you want relative to the input image
  2. Image Quality
    • Higher resolution settings produce better quality but take longer
    • More steps generally mean better quality but slower generation
    • Adjust the guidance scale based on how closely you want to follow the prompt
    • For img2img, ensure your input image has good quality for best results
  3. Cost Optimization
    • Start with lower resolution for testing
    • Use fewer steps during development
    • Generate one image at a time unless multiple variations are needed
    • Compress input images appropriately to reduce upload size

Error Handling

The API may return various error codes:
  • 400: Bad Request (invalid parameters)
  • 401: Unauthorized (invalid API key)
  • 429: Too Many Requests (rate limit exceeded)
  • 500: Internal Server Error
Always implement proper error handling in your applications:
try:
    result = generate_image(prompt)
except requests.exceptions.RequestException as e:
    if e.response:
        if e.response.status_code == 401:
            print("Invalid API key. Please check your credentials.")
        elif e.response.status_code == 429:
            print("Rate limit exceeded. Please wait before trying again.")
        else:
            print(f"API Error: {e.response.status_code}")
    else:
        print(f"Network Error: {str(e)}")