Leonardo AI Image Generation
"Leonardo AI: image generation API, fine-tuned models, canvas editing, texture generation, REST API"
Leonardo AI offers a REST API for image generation with a focus on fine-tuned models, game assets, and creative production workflows. The platform provides pre-trained model variants optimized for specific styles (photorealism, anime, concept art, 3D rendering) alongside the ability to train custom models on your own datasets. The API follows an async pattern: you submit a generation request, receive a generation ID, and poll for results. Leonardo differentiates itself with specialized features like texture generation for 3D models, canvas-based editing, and motion generation. The token-based pricing model means each generation costs a set number of tokens based on model, resolution, and features used. Use Leonardo when you need style-consistent output from fine-tuned models or game/creative asset pipelines. ## Key Points - **Use Alchemy for best quality**: Alchemy is Leonardo's enhanced generation pipeline. Enable it for production-quality output -- the token cost increase is worth the quality gain. - **Choose models by use case**: Use PhotoReal for photographs, Kino XL for cinematic scenes, Anime Pastel Dream for anime, and DreamShaper for versatile creative work. - **Batch efficiently**: Submit multiple generation requests and poll them in parallel. Leonardo allows concurrent generations based on your plan tier. - **Cache model IDs**: Fetch the model list once and cache it. Model IDs are stable and do not change. - **Use preset styles**: Presets like `CINEMATIC`, `CREATIVE`, `DYNAMIC`, `VIBRANT` adjust generation parameters behind the scenes for consistent aesthetic results. - **Monitor token usage**: Track token consumption per generation to predict costs. Higher resolution, more images, and Alchemy all increase token costs. - **Download and store images**: Leonardo-hosted URLs are temporary. Download generated images to your own storage for permanent access. - **Train on consistent datasets**: For custom models, use 10-20 high-quality, consistent images. Inconsistent training data produces unreliable models. - **Polling without delay**: Generations take 10-60 seconds. Polling every 500ms wastes API calls and may trigger rate limiting. Use 3-5 second intervals. - **Ignoring NSFW flags**: Generated images include an `nsfw` field. Always check and filter based on your platform requirements before displaying to users. - **Using default dimensions for all models**: Different models perform best at different resolutions. Check model documentation for recommended dimensions. - **Training models with too few images**: Custom model training with fewer than 5 images produces poor results. Use 10-20 curated images minimum.
skilldb get image-generation-services-skills/Leonardo AI Image GenerationFull skill: 393 linesLeonardo AI Image Generation
Core Philosophy
Leonardo AI offers a REST API for image generation with a focus on fine-tuned models, game assets, and creative production workflows. The platform provides pre-trained model variants optimized for specific styles (photorealism, anime, concept art, 3D rendering) alongside the ability to train custom models on your own datasets. The API follows an async pattern: you submit a generation request, receive a generation ID, and poll for results. Leonardo differentiates itself with specialized features like texture generation for 3D models, canvas-based editing, and motion generation. The token-based pricing model means each generation costs a set number of tokens based on model, resolution, and features used. Use Leonardo when you need style-consistent output from fine-tuned models or game/creative asset pipelines.
Setup
Configure the Leonardo AI REST client:
const LEONARDO_API_BASE = "https://cloud.leonardo.ai/api/rest/v1";
class LeonardoClient {
private apiKey: string;
constructor(apiKey: string) {
this.apiKey = apiKey;
}
async request<T>(
method: "GET" | "POST" | "DELETE",
endpoint: string,
body?: Record<string, unknown>
): Promise<T> {
const response = await fetch(`${LEONARDO_API_BASE}${endpoint}`, {
method,
headers: {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: body ? JSON.stringify(body) : undefined,
});
if (!response.ok) {
const error = await response.json().catch(() => ({ message: response.statusText }));
throw new Error(`Leonardo API error (${response.status}): ${JSON.stringify(error)}`);
}
return response.json() as Promise<T>;
}
}
const leonardo = new LeonardoClient(process.env.LEONARDO_API_KEY!);
Verify your setup by fetching user info:
async function getUserInfo(): Promise<{ id: string; tokenBalance: number }> {
const data = await leonardo.request<any>("GET", "/me");
return {
id: data.user_details[0].user.id,
tokenBalance: data.user_details[0].tokenRenewalDate
? data.user_details[0].apiConcurrencySlots
: 0,
};
}
Key Techniques
Text-to-Image Generation
interface GenerationRequest {
prompt: string;
negativePrompt?: string;
modelId?: string;
width?: number;
height?: number;
numImages?: number;
guidanceScale?: number;
numInferenceSteps?: number;
seed?: number;
presetStyle?: string;
alchemy?: boolean;
photoReal?: boolean;
photoRealVersion?: string;
}
interface GenerationResult {
id: string;
images: Array<{
id: string;
url: string;
nsfw: boolean;
}>;
status: string;
}
async function generateImages(options: GenerationRequest): Promise<string> {
const body: Record<string, unknown> = {
prompt: options.prompt,
negative_prompt: options.negativePrompt ?? "low quality, blurry, distorted",
modelId: options.modelId,
width: options.width ?? 1024,
height: options.height ?? 1024,
num_images: options.numImages ?? 1,
guidance_scale: options.guidanceScale ?? 7,
num_inference_steps: options.numInferenceSteps,
seed: options.seed,
presetStyle: options.presetStyle ?? "DYNAMIC",
alchemy: options.alchemy ?? true,
photoReal: options.photoReal ?? false,
photoRealVersion: options.photoRealVersion,
};
// Remove undefined fields
Object.keys(body).forEach((key) => {
if (body[key] === undefined) delete body[key];
});
const data = await leonardo.request<any>("POST", "/generations", body);
return data.sdGenerationJob.generationId as string;
}
async function getGeneration(generationId: string): Promise<GenerationResult> {
const data = await leonardo.request<any>("GET", `/generations/${generationId}`);
const gen = data.generations_by_pk;
return {
id: gen.id,
status: gen.status,
images: (gen.generated_images ?? []).map((img: any) => ({
id: img.id,
url: img.url,
nsfw: img.nsfw,
})),
};
}
Poll for Completion
async function generateAndWait(
options: GenerationRequest,
timeoutMs: number = 120_000
): Promise<GenerationResult> {
const generationId = await generateImages(options);
const startTime = Date.now();
while (Date.now() - startTime < timeoutMs) {
await new Promise((r) => setTimeout(r, 3000));
const result = await getGeneration(generationId);
if (result.status === "COMPLETE") {
return result;
}
if (result.status === "FAILED") {
throw new Error(`Generation ${generationId} failed`);
}
}
throw new Error(`Generation ${generationId} timed out after ${timeoutMs}ms`);
}
// Usage
const result = await generateAndWait({
prompt: "A fantasy castle on a floating island, concept art, detailed",
width: 1024,
height: 768,
numImages: 4,
alchemy: true,
presetStyle: "CREATIVE",
});
for (const image of result.images) {
console.log(`Image: ${image.url}`);
}
PhotoReal Mode
Leonardo's PhotoReal mode produces photorealistic images with enhanced detail:
async function generatePhotoReal(
prompt: string,
options?: { depth?: number; width?: number; height?: number }
): Promise<GenerationResult> {
return generateAndWait({
prompt,
photoReal: true,
photoRealVersion: "v2",
alchemy: true,
width: options?.width ?? 1024,
height: options?.height ?? 1024,
numImages: 1,
presetStyle: "CINEMATIC",
});
}
Using Fine-Tuned Platform Models
Leonardo provides pre-trained models optimized for different styles:
// Well-known Leonardo platform model IDs
const LEONARDO_MODELS = {
KINO_XL: "aa77f04e-3eec-4034-9c07-d0f619684628",
ANIME_PASTEL_DREAM: "1e60896f-3c26-4296-8ecc-53e2afecc132",
DREAM_SHAPER_V7: "ac614f96-1082-45bf-be9d-757f2d31c174",
LEONARDO_DIFFUSION_XL: "1e60896f-3c26-4296-8ecc-53e2afecc132",
} as const;
async function generateAnime(prompt: string): Promise<GenerationResult> {
return generateAndWait({
prompt,
modelId: LEONARDO_MODELS.ANIME_PASTEL_DREAM,
width: 1024,
height: 1024,
numImages: 1,
guidanceScale: 7,
presetStyle: "ANIME",
});
}
async function generateCinematic(prompt: string): Promise<GenerationResult> {
return generateAndWait({
prompt,
modelId: LEONARDO_MODELS.KINO_XL,
width: 1536,
height: 1024,
numImages: 1,
alchemy: true,
presetStyle: "CINEMATIC",
});
}
Training Custom Models
async function createDataset(name: string): Promise<string> {
const data = await leonardo.request<any>("POST", "/datasets", {
name,
description: `Training dataset: ${name}`,
});
return data.insert_datasets_one.id;
}
async function uploadTrainingImage(
datasetId: string,
imageUrl: string
): Promise<void> {
// Get a presigned upload URL
const upload = await leonardo.request<any>(
"POST",
`/datasets/${datasetId}/upload`,
{ extension: "jpg" }
);
// Upload image to presigned URL
const imageResponse = await fetch(imageUrl);
const imageBuffer = await imageResponse.arrayBuffer();
await fetch(upload.uploadDatasetImage.url, {
method: "PUT",
headers: { "Content-Type": "image/jpeg" },
body: imageBuffer,
});
// Confirm the upload
await leonardo.request("POST", `/datasets/${datasetId}/upload/gen`, {
id: upload.uploadDatasetImage.id,
});
}
async function trainModel(
name: string,
datasetId: string,
options?: { resolution?: number; numTrainEpochs?: number }
): Promise<string> {
const data = await leonardo.request<any>("POST", "/models", {
name,
datasetId,
instance_prompt: `a photo of sks ${name.toLowerCase()}`,
description: `Fine-tuned model: ${name}`,
modelType: "GENERAL",
sd_Version: "SDXL_0_9",
resolution: options?.resolution ?? 1024,
num_train_epochs: options?.numTrainEpochs ?? 30,
});
return data.sdTrainingJob.customModelId;
}
Texture Generation for 3D Models
async function generateTexture(
prompt: string,
modelAssetUrl?: string
): Promise<GenerationResult> {
const body: Record<string, unknown> = {
prompt,
preview: false,
seed: undefined,
negative_prompt: "low quality, tiling artifacts",
};
if (modelAssetUrl) {
body.modelAssetUrl = modelAssetUrl;
}
const data = await leonardo.request<any>("POST", "/generations-texture", body);
const generationId = data.textureGenerationJob.id;
// Poll for texture completion
const startTime = Date.now();
while (Date.now() - startTime < 120_000) {
await new Promise((r) => setTimeout(r, 5000));
const result = await leonardo.request<any>(
"GET",
`/generations-texture/${generationId}`
);
if (result.model_asset_texture_generations?.[0]?.status === "COMPLETE") {
return {
id: generationId,
status: "COMPLETE",
images: result.model_asset_texture_generations[0].texture_urls.map(
(url: string, i: number) => ({ id: `tex-${i}`, url, nsfw: false })
),
};
}
}
throw new Error("Texture generation timed out");
}
Image Upscaling
async function upscaleImage(imageId: string): Promise<string> {
const data = await leonardo.request<any>("POST", "/variations/upscale", {
id: imageId,
});
const variationId = data.sdUpscaleJob.id;
// Poll for upscaled result
const startTime = Date.now();
while (Date.now() - startTime < 60_000) {
await new Promise((r) => setTimeout(r, 3000));
const result = await leonardo.request<any>(
"GET",
`/variations/${variationId}`
);
if (result.generated_image_variation_generic?.[0]?.status === "COMPLETE") {
return result.generated_image_variation_generic[0].url;
}
}
throw new Error("Upscale timed out");
}
Best Practices
- Use Alchemy for best quality: Alchemy is Leonardo's enhanced generation pipeline. Enable it for production-quality output -- the token cost increase is worth the quality gain.
- Choose models by use case: Use PhotoReal for photographs, Kino XL for cinematic scenes, Anime Pastel Dream for anime, and DreamShaper for versatile creative work.
- Batch efficiently: Submit multiple generation requests and poll them in parallel. Leonardo allows concurrent generations based on your plan tier.
- Cache model IDs: Fetch the model list once and cache it. Model IDs are stable and do not change.
- Use preset styles: Presets like
CINEMATIC,CREATIVE,DYNAMIC,VIBRANTadjust generation parameters behind the scenes for consistent aesthetic results. - Monitor token usage: Track token consumption per generation to predict costs. Higher resolution, more images, and Alchemy all increase token costs.
- Download and store images: Leonardo-hosted URLs are temporary. Download generated images to your own storage for permanent access.
- Train on consistent datasets: For custom models, use 10-20 high-quality, consistent images. Inconsistent training data produces unreliable models.
Anti-Patterns
- Polling without delay: Generations take 10-60 seconds. Polling every 500ms wastes API calls and may trigger rate limiting. Use 3-5 second intervals.
- Ignoring NSFW flags: Generated images include an
nsfwfield. Always check and filter based on your platform requirements before displaying to users. - Using default dimensions for all models: Different models perform best at different resolutions. Check model documentation for recommended dimensions.
- Training models with too few images: Custom model training with fewer than 5 images produces poor results. Use 10-20 curated images minimum.
- Skipping negative prompts: Negative prompts significantly improve output quality. Always include at least basic quality exclusions like "low quality, blurry, distorted".
- Not handling generation failures: Generations can fail due to content moderation, capacity limits, or model issues. Always check the status field and handle failures gracefully.
- Overusing PhotoReal mode: PhotoReal costs more tokens and is slower. Only use it when photorealism is specifically required. Standard Alchemy mode handles most creative use cases well.
Install this skill directly: skilldb add image-generation-services-skills
Related Skills
Adobe Firefly API
"Adobe Firefly: text-to-image generation, generative fill, generative expand, style reference, content credentials, REST API"
Cloudinary Image Generation & Manipulation
"Cloudinary: image and video upload, transformation, AI-based generation, background removal, CDN delivery, URL-based API"
DALL-E Image Generation
"DALL-E API (OpenAI): image generation, editing, variations, quality/style params, size options, Node SDK"
fal.ai Image Generation
"fal.ai: fast inference, Flux, realtime image gen, queue API, webhooks, JavaScript SDK, serverless GPU"
Imgix Image Processing
"Imgix: real-time image processing and CDN, URL-based transformations, resizing, cropping, watermarking, face detection, format optimization"
Replicate Image Generation
Replicate for image generation: Flux, SDXL, img2img, inpainting, upscaling, predictions API, webhooks, streaming