API Документация v2.0

AI ImageGenerator API v2.0 - мощный REST API для генерации изображений с помощью передовых AI моделей. Создавайте уникальные изображения, трансформируйте существующие и масштабируйте их качество.

🚀 Ключевые возможности

  • Text-to-Image - генерация изображений по текстовому описанию
  • 🎨 Image-to-Image - трансформация и стилизация изображений
  • 🔍 Upscaling - увеличение разрешения с улучшением качества
  • 🎭 LoRA модели - специализированные стили и эффекты
  • 📊 Детальная аналитика - статистика использования
  • 🔄 Real-time обновления - WebSocket поддержка
  • 📦 Batch обработка - массовая генерация
  • 🔔 Webhooks - автоматические уведомления
  • 🎯 Smart Upscale - интеллектуальное масштабирование

📍 Базовая информация

Базовый URL https://image.nalitek.com
Версия API 2.0
Формат данных JSON
Аутентификация API Key в заголовке X-API-Key

Быстрый старт

Шаг 1: Получите API ключ

Зарегистрируйтесь на сайте и получите API ключ в личном кабинете.

Шаг 2: Сделайте первый запрос

import requests

api_key = "your-api-key"
url = "https://image.nalitek.com/api/generate"

response = requests.post(url,
    json={
        "prompt": "beautiful sunset over mountains, professional photography",
        "width": 512,           # Быстрая генерация в низком разрешении
        "height": 512,
        "smart_upscale": True,  # Автоматическое увеличение до HD
        "target_width": 2048,
        "target_height": 2048,
        "model": "flux-dev"
    },
    headers={"X-API-Key": api_key}
)

data = response.json()
print(f"Image ID: {data['image_id']}")
print(f"Generation ID: {data['generation_id']}")
print(f"Progress URL: {data['progress_url']}")
const apiKey = 'your-api-key';
const url = 'https://image.nalitek.com/api/generate';

fetch(url, {
    method: 'POST',
    headers: {
        'X-API-Key': apiKey,
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        prompt: 'beautiful sunset over mountains, professional photography',
        width: 512,           // Быстрая генерация в низком разрешении
        height: 512,
        smart_upscale: true,  // Автоматическое увеличение до HD
        target_width: 2048,
        target_height: 2048,
        model: 'flux-dev'
    })
})
.then(res => res.json())
.then(data => {
    console.log(`Image ID: ${data.image_id}`);
    console.log(`Generation ID: ${data.generation_id}`);
});
$apiKey = 'your-api-key';
$url = 'https://image.nalitek.com/api/generate';

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'prompt' => 'beautiful sunset over mountains, professional photography',
    'width' => 512,           // Быстрая генерация в низком разрешении
    'height' => 512,
    'smart_upscale' => true,  // Автоматическое увеличение до HD
    'target_width' => 2048,
    'target_height' => 2048,
    'model' => 'flux-dev'
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'X-API-Key: ' . $apiKey,
    'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
$data = json_decode($response, true);
echo "Image ID: " . $data['image_id'];
curl -X POST https://image.nalitek.com/api/generate \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "beautiful sunset over mountains, professional photography",
    "width": 512,
    "height": 512,
    "smart_upscale": true,
    "target_width": 2048,
    "target_height": 2048,
    "model": "flux-dev"
  }'

Аутентификация

Важно! Все запросы к API требуют аутентификации через API ключ.

🔐 Использование API ключа

Передавайте ключ в заголовке X-API-Key каждого запроса:

X-API-Key: your-api-key-here

🛡️ Рекомендации по безопасности

  • 🔒 Никогда не передавайте API ключ в URL параметрах
  • 🔐 Не храните ключ в публичных репозиториях
  • 🔑 Используйте переменные окружения для хранения
  • 🔄 Регулярно обновляйте ключи
  • 📊 Мониторьте использование API в личном кабинете
  • 🚫 Ограничивайте доступ по IP для production ключей

Генерация изображений

POST /api/generate

Создание изображения по текстовому описанию с поддержкой различных моделей и стилей.

Параметры запроса

Параметр Тип Описание По умолчанию
prompt required string Текстовое описание желаемого изображения -
negative_prompt optional string Что исключить из генерации ""
width optional integer Ширина изображения (512-2048, кратно 64) 1024
height optional integer Высота изображения (512-2048, кратно 64) 1024
steps optional integer Количество шагов генерации (10-100) 30
guidance_scale optional float Сила следования промпту (1.0-20.0) 7.5
seed optional integer Seed для воспроизводимости (-1 = random) -1
lora_ids optional array Массив ID LoRA моделей для стилизации []
format optional string Формат изображения (png/jpeg/webp) png
model optional string Модель генерации (flux-dev/flux-schnell) flux-dev
smart_upscale optional boolean Автоматическое увеличение с коэффициентом 2x. При запросе размера >512px генерирует в половинном размере и масштабирует до целевого true

Ответ успешной генерации

{
    "image_id": 4738,
    "status": "generating",
    "generation_id": "gen_17368567894738",
    "url": null,
    "thumbnail_url": null,
    "prompt": "beautiful sunset over mountains, professional photography, golden hour lighting",
    "seed": 8472659,
    "dimensions": "1024x1024",
    "estimated_time": 28,
    "progress_url": "/api/images/4738",
    "websocket_channel": "generation_4738"
}

Примеры использования

# Простая генерация
curl -X POST https://image.nalitek.com/api/generate \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "beautiful sunset over mountains",
    "width": 1024,
    "height": 1024,
    "steps": 30
  }'

# С дополнительными параметрами
curl -X POST https://image.nalitek.com/api/generate \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "futuristic city at night, neon lights, cyberpunk style",
    "negative_prompt": "blurry, low quality, distorted",
    "width": 1920,
    "height": 1080,
    "steps": 50,
    "guidance_scale": 8.5,
    "seed": 42,
    "model": "flux-dev",
    "format": "png"
  }'

# С Smart Upscale
curl -X POST https://image.nalitek.com/api/generate \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "ultra detailed portrait of a woman",
    "width": 512,
    "height": 512,
    "steps": 30,
    "smart_upscale": true,
    "target_width": 2048,
    "target_height": 2048
  }'

# С LoRA моделью
curl -X POST https://image.nalitek.com/api/generate \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "anime character, detailed eyes",
    "width": 1024,
    "height": 1024,
    "lora_id": "anime-style-v2",
    "steps": 30
  }'

# Проверка статуса генерации
curl -X GET https://image.nalitek.com/api/images/4738 \
  -H "X-API-Key: your-api-key"
import requests
import time

class FluxAPI:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://image.nalitek.com"
        self.headers = {"X-API-Key": api_key, "Content-Type": "application/json"}

    def generate_image(self, prompt, **kwargs):
        """Генерация изображения по промпту"""
        url = f"{self.base_url}/api/generate"

        data = {
            "prompt": prompt,
            "negative_prompt": kwargs.get("negative_prompt", ""),
            "width": kwargs.get("width", 1024),
            "height": kwargs.get("height", 1024),
            "steps": kwargs.get("steps", 30),
            "guidance_scale": kwargs.get("guidance_scale", 7.5),
            "seed": kwargs.get("seed", -1),
            "model": kwargs.get("model", "flux-dev"),
            "format": kwargs.get("format", "png")
        }

        if "lora_id" in kwargs:
            data["lora_id"] = kwargs["lora_id"]
            
        # Добавляем параметры smart_upscale если указаны
        if kwargs.get("smart_upscale"):
            data["smart_upscale"] = kwargs.get("smart_upscale")
            if "target_width" in kwargs:
                data["target_width"] = kwargs["target_width"]
            if "target_height" in kwargs:
                data["target_height"] = kwargs["target_height"]

        response = requests.post(url, json=data, headers=self.headers)

        if response.status_code == 201:
            result = response.json()
            print(f"✅ Success! Generation ID: {result['generation_id']}")
            print(f"📍 Progress URL: {result['progress_url']}")
            print(f"🌱 Seed: {result['seed']}")
            return result
        else:
            print(f"❌ Error: {response.status_code}")
            print(response.json())
            return None

    def check_status(self, image_id):
        """Проверка статуса генерации"""
        url = f"{self.base_url}/api/images/{image_id}"
        response = requests.get(url, headers=self.headers)
        return response.json()

    def wait_for_completion(self, image_id, timeout=120):
        """Ожидание завершения генерации"""
        start_time = time.time()

        while time.time() - start_time < timeout:
            status = self.check_status(image_id)

            if status['status'] == 'completed':
                print(f"✅ Completed! URL: {status['url']}")
                return status
            elif status['status'] == 'failed':
                print(f"❌ Failed: {status.get('error_message')}")
                return status

            time.sleep(2)

        print(f"⏱️ Timeout after {timeout} seconds")
        return None

# Использование
api = FluxAPI("your-api-key")

# Пример 1: Простая генерация (smart upscale включен по умолчанию)
# При запросе 1024x1024, сгенерирует в 512x512 и масштабирует до 1024x1024
image = api.generate_image(
    prompt="cyberpunk city at night, neon lights",
    width=1024,             # Целевой размер 1024x1024
    height=1024,            # Автоматически генерирует в 512x512 и масштабирует
    steps=30,
    guidance_scale=7.5
)

# Пример 2: Отключение smart upscale для прямой генерации
image = api.generate_image(
    prompt="detailed portrait",
    width=512,
    height=512,
    smart_upscale=False,    # Генерировать напрямую в 512x512
    steps=30
)
if image:
    result = api.wait_for_completion(image['image_id'])
    print(f"Generated and upscaled to {result['dimensions']}")

# Расширенная генерация с LoRA
image = api.generate_image(
    prompt="beautiful japanese garden, cherry blossoms, koi pond",
    negative_prompt="blurry, low quality, distorted",
    width=1024,
    height=1024,
    steps=50,
    guidance_scale=8.0,
    model="flux-dev",
    lora_id="anime-style-v2"
)

# Генерация с автоматическим upscale
# Генерируем в низком разрешении для скорости, затем автоматически увеличиваем
image = api.generate_image(
    prompt="ultra realistic portrait, 8k quality, professional photography",
    width=512,           # Генерируем в низком разрешении
    height=512,
    smart_upscale=True,  # Включаем автоматический upscale
    target_width=2048,   # Целевой размер после upscale
    target_height=2048,
    steps=30,
    model="flux-dev"
)
if image:
    result = api.wait_for_completion(image['image_id'])
    print(f"Image automatically upscaled to {result['dimensions']}")
class FluxAPI {
    constructor(apiKey) {
        this.apiKey = apiKey;
        this.baseUrl = 'https://image.nalitek.com';
    }

    async generateImage(prompt, options = {}) {
        const url = `${this.baseUrl}/api/generate`;

        const data = {
            prompt: prompt,
            negative_prompt: options.negative_prompt || '',
            width: options.width || 1024,
            height: options.height || 1024,
            steps: options.steps || 30,
            guidance_scale: options.guidance_scale || 7.5,
            seed: options.seed || -1,
            model: options.model || 'flux-dev',
            format: options.format || 'png'
        };

        if (options.lora_id) {
            data.lora_id = options.lora_id;
        }
        
        // Добавляем параметры smart_upscale если указаны
        if (options.smart_upscale) {
            data.smart_upscale = true;
            if (options.target_width) {
                data.target_width = options.target_width;
            }
            if (options.target_height) {
                data.target_height = options.target_height;
            }
        }

        try {
            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'X-API-Key': this.apiKey,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(data)
            });

            if (response.status === 201) {
                const result = await response.json();
                console.log(`✅ Success! Generation ID: ${result.generation_id}`);
                console.log(`📍 Progress URL: ${result.progress_url}`);
                return result;
            } else {
                const error = await response.json();
                console.error(`❌ Error: ${response.status}`, error);
                return null;
            }
        } catch (error) {
            console.error('Request failed:', error);
            return null;
        }
    }

    async checkStatus(imageId) {
        const url = `${this.baseUrl}/api/images/${imageId}`;
        const response = await fetch(url, {
            headers: { 'X-API-Key': this.apiKey }
        });
        return response.json();
    }

    async waitForCompletion(imageId, timeout = 120000) {
        const startTime = Date.now();

        while (Date.now() - startTime < timeout) {
            const status = await this.checkStatus(imageId);

            if (status.status === 'completed') {
                console.log(`✅ Completed! URL: ${status.url}`);
                return status;
            } else if (status.status === 'failed') {
                console.error(`❌ Failed: ${status.error_message}`);
                return status;
            }

            await new Promise(resolve => setTimeout(resolve, 2000));
        }

        console.error(`⏱️ Timeout after ${timeout}ms`);
        return null;
    }
}

// Использование
const api = new FluxAPI('your-api-key');

// Простая генерация с автоматическим upscale
async function generateSimple() {
    const image = await api.generateImage('cyberpunk city at night, neon lights', {
        width: 512,           // Генерируем быстро в низком разрешении
        height: 512,
        smart_upscale: true,  // Автоматически увеличиваем после генерации
        target_width: 2048,   // Финальный размер
        target_height: 2048
    });
    if (image) {
        const result = await api.waitForCompletion(image.image_id);
        console.log(`Generated and upscaled to ${result.dimensions}`);
    }
}

// Расширенная генерация
async function generateAdvanced() {
    const image = await api.generateImage('beautiful japanese garden', {
        negative_prompt: 'blurry, low quality',
        width: 1024,
        height: 1024,
        steps: 50,
        guidance_scale: 8.0,
        model: 'flux-dev',
        lora_id: 'anime-style-v2'
    });

    if (image) {
        const result = await api.waitForCompletion(image.image_id);
    }
}

// Генерация с автоматическим upscale
async function generateWithSmartUpscale() {
    const image = await api.generateImage('ultra realistic portrait, 8k quality', {
        width: 512,           // Генерируем в низком разрешении для скорости
        height: 512,
        smart_upscale: true,  // Включаем автоматический upscale
        target_width: 2048,   // Целевой размер после upscale
        target_height: 2048,
        steps: 30,
        model: 'flux-dev'
    });

    if (image) {
        const result = await api.waitForCompletion(image.image_id);
        console.log(`Image automatically upscaled to ${result.dimensions}`);
    }
}

generateSimple();
generateAdvanced();
generateWithSmartUpscale();
<?php

class FluxAPI {
    private $apiKey;
    private $baseUrl = 'https://image.nalitek.com';

    public function __construct($apiKey) {
        $this->apiKey = $apiKey;
    }

    public function generateImage($prompt, $options = []) {
        $url = $this->baseUrl . '/api/generate';

        $data = array_merge([
            'prompt' => $prompt,
            'negative_prompt' => '',
            'width' => 1024,
            'height' => 1024,
            'steps' => 30,
            'guidance_scale' => 7.5,
            'seed' => -1,
            'model' => 'flux-dev',
            'format' => 'png'
        ], $options);
        
        // Добавляем параметры smart_upscale если указаны
        if (isset($options['smart_upscale']) && $options['smart_upscale']) {
            $data['smart_upscale'] = true;
            if (isset($options['target_width'])) {
                $data['target_width'] = $options['target_width'];
            }
            if (isset($options['target_height'])) {
                $data['target_height'] = $options['target_height'];
            }
        }

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'X-API-Key: ' . $this->apiKey,
            'Content-Type: application/json'
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 201) {
            $result = json_decode($response, true);
            echo "✅ Success! Generation ID: " . $result['generation_id'] . "\n";
            echo "📍 Progress URL: " . $result['progress_url'] . "\n";
            return $result;
        } else {
            echo "❌ Error: HTTP $httpCode\n";
            echo $response . "\n";
            return null;
        }
    }

    public function checkStatus($imageId) {
        $url = $this->baseUrl . '/api/images/' . $imageId;

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $this->apiKey]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        curl_close($ch);

        return json_decode($response, true);
    }

    public function waitForCompletion($imageId, $timeout = 120) {
        $startTime = time();

        while (time() - $startTime < $timeout) {
            $status = $this->checkStatus($imageId);

            if ($status['status'] === 'completed') {
                echo "✅ Completed! URL: " . $status['url'] . "\n";
                return $status;
            } elseif ($status['status'] === 'failed') {
                echo "❌ Failed: " . $status['error_message'] . "\n";
                return $status;
            }

            sleep(2);
        }

        echo "⏱️ Timeout after $timeout seconds\n";
        return null;
    }
}

// Использование
$api = new FluxAPI('your-api-key');

// Простая генерация с автоматическим upscale
$image = $api->generateImage('cyberpunk city at night, neon lights', [
    'width' => 512,           // Генерируем быстро в низком разрешении
    'height' => 512,
    'smart_upscale' => true,  // Автоматически увеличиваем после генерации
    'target_width' => 2048,   // Финальный размер
    'target_height' => 2048
]);
if ($image) {
    $result = $api->waitForCompletion($image['image_id']);
    echo "Generated and upscaled to " . $result['dimensions'] . "\n";
}

// Расширенная генерация
$image = $api->generateImage('beautiful japanese garden', [
    'negative_prompt' => 'blurry, low quality',
    'width' => 1024,
    'height' => 1024,
    'steps' => 50,
    'guidance_scale' => 8.0,
    'model' => 'flux-dev',
    'lora_id' => 'anime-style-v2'
]);

if ($image) {
    $result = $api->waitForCompletion($image['image_id']);
}

// Генерация с автоматическим upscale
$image = $api->generateImage('ultra realistic portrait, 8k quality', [
    'width' => 512,           // Генерируем в низком разрешении
    'height' => 512,
    'smart_upscale' => true,  // Включаем автоматический upscale
    'target_width' => 2048,   // Целевой размер после upscale
    'target_height' => 2048,
    'steps' => 30,
    'model' => 'flux-dev'
]);

if ($image) {
    $result = $api->waitForCompletion($image['image_id']);
    echo "Image automatically upscaled to " . $result['dimensions'] . "\n";
}

Трансформация изображений

POST /api/img2img

Трансформация существующего изображения на основе текстового описания.

Параметры запроса (multipart/form-data)

Параметр Тип Описание По умолчанию
image required file Файл изображения (max 10MB, formats: png/jpg/webp) -
prompt required string Описание желаемой трансформации -
negative_prompt optional string Что исключить из генерации ""
strength optional float Сила изменения (0.0-1.0) 0.75
seed optional integer Seed для воспроизводимости -1
steps optional integer Количество шагов (10-100) 30
guidance_scale optional float Сила следования промпту (1.0-20.0) 7.5
width optional integer Ширина результата (512-2048) 1024
height optional integer Высота результата (512-2048) 1024
format optional string Формат результата (png/jpeg/webp) png
model optional string Модель (flux-dev/flux-schnell) flux-dev
lora_models optional array Список ID LoRA моделей []
smart_upscale optional boolean Автоматический upscale после трансформации false
target_width optional integer Целевая ширина для smart_upscale null
target_height optional integer Целевая высота для smart_upscale null

Примеры использования

# Простая трансформация изображения
curl -X POST https://image.nalitek.com/api/img2img \
  -H "X-API-Key: your-api-key" \
  -F "image=@/path/to/image.jpg" \
  -F "prompt=transform to cyberpunk style" \
  -F "strength=0.75" \
  -F "steps=30"

# С дополнительными параметрами
curl -X POST https://image.nalitek.com/api/img2img \
  -H "X-API-Key: your-api-key" \
  -F "image=@/path/to/portrait.png" \
  -F "prompt=make it look like an oil painting, artistic, brush strokes visible" \
  -F "negative_prompt=digital, photo, realistic" \
  -F "strength=0.8" \
  -F "steps=50" \
  -F "guidance_scale=8.5" \
  -F "width=1920" \
  -F "height=1080" \
  -F "seed=42" \
  -F "model=flux-dev" \
  -F "format=png"

# С Smart Upscale
curl -X POST https://image.nalitek.com/api/img2img \
  -H "X-API-Key: your-api-key" \
  -F "image=@/path/to/small_image.jpg" \
  -F "prompt=enhance details, make it sharper" \
  -F "strength=0.5" \
  -F "width=512" \
  -F "height=512" \
  -F "smart_upscale=true" \
  -F "target_width=2048" \
  -F "target_height=2048"

# С несколькими LoRA моделями
curl -X POST https://image.nalitek.com/api/img2img \
  -H "X-API-Key: your-api-key" \
  -F "image=@/path/to/photo.jpg" \
  -F "prompt=anime style character" \
  -F "strength=0.7" \
  -F "lora_models[]=anime-style-v2" \
  -F "lora_models[]=detailed-eyes-v1" \
  -F "steps=30"

# Сохранение результата в файл
curl -X POST https://image.nalitek.com/api/img2img \
  -H "X-API-Key: your-api-key" \
  -F "image=@input.jpg" \
  -F "prompt=futuristic version" \
  -F "strength=0.75" \
  -o output.json

# Затем получить изображение по URL из ответа
curl -o result.png "https://image.nalitek.com/uploads/images/flux_20250115_123456_abc123.png"
import requests

def transform_image(api_key, image_path, prompt, **kwargs):
    """Трансформация изображения с img2img"""
    url = "https://image.nalitek.com/api/img2img"
    headers = {"X-API-Key": api_key}

    # Подготовка файла
    with open(image_path, 'rb') as f:
        files = {'image': (image_path, f, 'image/png')}

        # Подготовка данных
        data = {
            'prompt': prompt,
            'negative_prompt': kwargs.get('negative_prompt', ''),
            'strength': kwargs.get('strength', 0.75),
            'steps': kwargs.get('steps', 30),
            'seed': kwargs.get('seed', -1),
            'width': kwargs.get('width', 1024),
            'height': kwargs.get('height', 1024),
            'guidance_scale': kwargs.get('guidance_scale', 7.5),
            'format': kwargs.get('format', 'png'),
            'model': kwargs.get('model', 'flux-dev')
        }
        
        # Добавляем параметры smart_upscale если указаны
        if kwargs.get('smart_upscale'):
            data['smart_upscale'] = 'true'
            if kwargs.get('target_width'):
                data['target_width'] = kwargs.get('target_width')
            if kwargs.get('target_height'):
                data['target_height'] = kwargs.get('target_height')
        
        # Добавляем lora_models если указаны
        if kwargs.get('lora_models'):
            data['lora_models[]'] = kwargs.get('lora_models')

        # Отправка запроса
        response = requests.post(url, headers=headers, files=files, data=data)

    if response.status_code == 201:
        result = response.json()
        print(f"✅ Success! Image URL: {result['url']}")
        print(f"📐 Dimensions: {result['dimensions']}")
        print(f"🌱 Seed: {result['seed']}")
        return result
    else:
        print(f"❌ Error: {response.status_code}")
        print(response.json())
        return None

# Использование с автоматическим upscale
result = transform_image(
    api_key="your-api-key",
    image_path="input.png",
    prompt="transform to cyberpunk style, neon lights, ultra HD",
    negative_prompt="blurry, low quality",
    width=512,              # Трансформируем в низком разрешении для скорости
    height=512,
    smart_upscale=True,     # Автоматически увеличиваем после трансформации
    target_width=2048,      # Финальный размер
    target_height=2048,
    strength=0.8,
    steps=40
)

# Загрузка результата
if result:
    image_url = result['url']
    response = requests.get(image_url)
    with open('output.png', 'wb') as f:
        f.write(response.content)
    print("✅ Image saved as output.png")

# Трансформация с автоматическим upscale
result = transform_image(
    api_key="your-api-key",
    image_path="input.png",
    prompt="ultra HD quality, 8k resolution",
    smart_upscale=True,     # Включаем автоматический upscale
    target_width=2048,      # Целевой размер
    target_height=2048,
    strength=0.7,
    steps=30
)
if result:
    print(f"Image transformed and upscaled to {result['dimensions']}")
async function transformImage(apiKey, imageFile, prompt, options = {}) {
    const url = 'https://image.nalitek.com/api/img2img';

    // Создание FormData
    const formData = new FormData();
    formData.append('image', imageFile);
    formData.append('prompt', prompt);
    formData.append('negative_prompt', options.negative_prompt || '');
    formData.append('strength', options.strength || 0.75);
    formData.append('steps', options.steps || 30);
    formData.append('seed', options.seed || -1);
    formData.append('width', options.width || 1024);
    formData.append('height', options.height || 1024);
    formData.append('guidance_scale', options.guidance_scale || 7.5);
    formData.append('format', options.format || 'png');
    formData.append('model', options.model || 'flux-dev');
    
    // Добавляем параметры smart_upscale если указаны
    if (options.smart_upscale) {
        formData.append('smart_upscale', 'true');
        if (options.target_width) {
            formData.append('target_width', options.target_width);
        }
        if (options.target_height) {
            formData.append('target_height', options.target_height);
        }
    }
    
    // Добавляем lora_models если указаны
    if (options.lora_models && options.lora_models.length > 0) {
        options.lora_models.forEach(lora => {
            formData.append('lora_models[]', lora);
        });
    }

    try {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'X-API-Key': apiKey
            },
            body: formData
        });

        if (response.status === 201) {
            const result = await response.json();
            console.log(`✅ Success! Image URL: ${result.url}`);
            console.log(`📐 Dimensions: ${result.dimensions}`);
            console.log(`🌱 Seed: ${result.seed}`);
            return result;
        } else {
            const error = await response.json();
            console.error(`❌ Error: ${response.status}`, error);
            return null;
        }
    } catch (error) {
        console.error('Request failed:', error);
        return null;
    }
}

// Использование с input элементом
document.getElementById('fileInput').addEventListener('change', async (event) => {
    const file = event.target.files[0];
    if (file) {
        // Пример с автоматическим upscale
        const result = await transformImage(
            'your-api-key',
            file,
            'transform to cyberpunk style, neon lights, ultra HD',
            {
                width: 512,           // Трансформируем в низком разрешении
                height: 512,
                smart_upscale: true,  // Автоматически увеличиваем после
                target_width: 2048,   // Финальный размер
                target_height: 2048,
                negative_prompt: 'blurry, low quality',
                strength: 0.8,
                steps: 40
            }
        );
        
        // Пример с автоматическим upscale
        const resultWithUpscale = await transformImage(
            'your-api-key',
            file,
            'ultra HD quality, 8k resolution',
            {
                width: 512,           // Трансформируем в низком разрешении
                height: 512,
                smart_upscale: true,  // Включаем автоматический upscale
                target_width: 2048,   // Целевой размер
                target_height: 2048,
                strength: 0.7,
                steps: 30
            }
        );

        if (result) {
            // Отобразить результат
            document.getElementById('resultImage').src = result.url;
        }
    }
});

// Использование с URL изображения
async function transformFromUrl(imageUrl) {
    const response = await fetch(imageUrl);
    const blob = await response.blob();
    const file = new File([blob], 'image.png', { type: 'image/png' });

    return await transformImage(
        'your-api-key',
        file,
        'make it artistic painting style'
    );
}
<?php

function transformImage($apiKey, $imagePath, $prompt, $options = []) {
    $url = 'https://image.nalitek.com/api/img2img';

    // Подготовка файла
    $cFile = new CURLFile($imagePath, mime_content_type($imagePath), basename($imagePath));

    // Подготовка данных
    $data = array_merge([
        'image' => $cFile,
        'prompt' => $prompt,
        'negative_prompt' => '',
        'strength' => 0.75,
        'steps' => 30,
        'seed' => -1,
        'width' => 1024,
        'height' => 1024,
        'guidance_scale' => 7.5,
        'format' => 'png',
        'model' => 'flux-dev'
    ], $options);
    
    // Добавляем параметры smart_upscale если указаны
    if (isset($options['smart_upscale']) && $options['smart_upscale']) {
        $data['smart_upscale'] = 'true';
        if (isset($options['target_width'])) {
            $data['target_width'] = $options['target_width'];
        }
        if (isset($options['target_height'])) {
            $data['target_height'] = $options['target_height'];
        }
    }
    
    // Добавляем lora_models если указаны
    if (isset($options['lora_models']) && is_array($options['lora_models'])) {
        foreach ($options['lora_models'] as $index => $lora) {
            $data["lora_models[$index]"] = $lora;
        }
    }

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $apiKey]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($httpCode === 201) {
        $result = json_decode($response, true);
        echo "✅ Success! Image URL: " . $result['url'] . "\n";
        echo "📐 Dimensions: " . $result['dimensions'] . "\n";
        echo "🌱 Seed: " . $result['seed'] . "\n";
        return $result;
    } else {
        echo "❌ Error: HTTP $httpCode\n";
        echo $response . "\n";
        return null;
    }
}

// Использование с автоматическим upscale
$result = transformImage(
    'your-api-key',
    'input.png',
    'transform to cyberpunk style, neon lights, ultra HD',
    [
        'width' => 512,           // Трансформируем в низком разрешении
        'height' => 512,
        'smart_upscale' => true,  // Автоматически увеличиваем после
        'target_width' => 2048,   // Финальный размер
        'target_height' => 2048,
        'negative_prompt' => 'blurry, low quality',
        'strength' => 0.8,
        'steps' => 40
    ]
);

// Загрузка результата
if ($result) {
    $imageContent = file_get_contents($result['url']);
    file_put_contents('output.png', $imageContent);
    echo "✅ Image saved as output.png\n";
}

// Пример с автоматическим upscale
$resultWithUpscale = transformImage(
    'your-api-key',
    'input.png',
    'ultra HD quality, 8k resolution',
    [
        'width' => 512,           // Трансформируем в низком разрешении
        'height' => 512,
        'smart_upscale' => true,  // Включаем автоматический upscale
        'target_width' => 2048,   // Целевой размер
        'target_height' => 2048,
        'strength' => 0.7,
        'steps' => 30
    ]
);

if ($resultWithUpscale) {
    echo "Image transformed and upscaled to " . $resultWithUpscale['dimensions'] . "\n";
}

// Пример с загрузкой через форму
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {
    $uploadedFile = $_FILES['image']['tmp_name'];
    $prompt = $_POST['prompt'] ?? 'transform to artistic style';

    $result = transformImage(
        'your-api-key',
        $uploadedFile,
        $prompt,
        [
            'strength' => floatval($_POST['strength'] ?? 0.75),
            'steps' => intval($_POST['steps'] ?? 30)
        ]
    );

    if ($result) {
        header('Content-Type: application/json');
        echo json_encode($result);
    }
}

Увеличение разрешения

POST /api/upscale/{image_id}

Увеличение разрешения существующего изображения с улучшением качества.

Параметры запроса

Параметр Тип Описание По умолчанию
scale optional integer Коэффициент увеличения (2 или 4) 2
face_enhance optional boolean Улучшение лиц с GFPGAN false
denoise optional float Уровень удаления шума (0.0-1.0) 0.5

Примеры использования

# Простое увеличение в 2 раза
curl -X POST https://image.nalitek.com/api/upscale/4738 \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "scale": 2
  }'

# Увеличение в 4 раза
curl -X POST https://image.nalitek.com/api/upscale/4738 \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "scale": 4,
    "denoise": 0.5
  }'

# С улучшением лиц (для портретов)
curl -X POST https://image.nalitek.com/api/upscale/4738 \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "scale": 4,
    "face_enhance": true,
    "denoise": 0.7
  }'

# Проверка статуса upscale
curl -X GET https://image.nalitek.com/api/images/4739 \
  -H "X-API-Key: your-api-key"

# Скачивание результата
curl -X GET https://image.nalitek.com/api/images/4739 \
  -H "X-API-Key: your-api-key" \
  | jq -r '.url' \
  | xargs curl -o upscaled_image.png
import requests

def upscale_image(api_key, image_id, scale=2, face_enhance=False, denoise=0.5):
    """Увеличение разрешения изображения"""
    url = f"https://image.nalitek.com/api/upscale/{image_id}"
    headers = {
        "X-API-Key": api_key,
        "Content-Type": "application/json"
    }

    data = {
        "scale": scale,
        "face_enhance": face_enhance,
        "denoise": denoise
    }

    response = requests.post(url, json=data, headers=headers)

    if response.status_code == 201:
        result = response.json()
        print(f"✅ Success! Upscaled URL: {result['url']}")
        print(f"📐 New dimensions: {result['dimensions']}")
        print(f"🔍 Scale: {result['scale']}x")
        return result
    else:
        print(f"❌ Error: {response.status_code}")
        print(response.json())
        return None

# Простое увеличение в 2 раза
result = upscale_image(
    api_key="your-api-key",
    image_id=4738,
    scale=2
)

# Увеличение в 4 раза с улучшением лиц
result = upscale_image(
    api_key="your-api-key",
    image_id=4738,
    scale=4,
    face_enhance=True,
    denoise=0.7
)
async function upscaleImage(apiKey, imageId, options = {}) {
    const url = `https://image.nalitek.com/api/upscale/${imageId}`;

    const data = {
        scale: options.scale || 2,
        face_enhance: options.face_enhance || false,
        denoise: options.denoise || 0.5
    };

    try {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'X-API-Key': apiKey,
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(data)
        });

        if (response.status === 201) {
            const result = await response.json();
            console.log(`✅ Success! Upscaled URL: ${result.url}`);
            console.log(`📐 New dimensions: ${result.dimensions}`);
            console.log(`🔍 Scale: ${result.scale}x`);
            return result;
        } else {
            const error = await response.json();
            console.error(`❌ Error: ${response.status}`, error);
            return null;
        }
    } catch (error) {
        console.error('Request failed:', error);
        return null;
    }
}

// Простое увеличение в 2 раза
upscaleImage('your-api-key', 123);

// Увеличение в 4 раза с улучшением лиц
upscaleImage('your-api-key', 123, {
    scale: 4,
    face_enhance: true,
    denoise: 0.7
});
<?php

function upscaleImage($apiKey, $imageId, $options = []) {
    $url = "https://image.nalitek.com/api/upscale/$imageId";

    $data = array_merge([
        'scale' => 2,
        'face_enhance' => false,
        'denoise' => 0.5
    ], $options);

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'X-API-Key: ' . $apiKey,
        'Content-Type: application/json'
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($httpCode === 201) {
        $result = json_decode($response, true);
        echo "✅ Success! Upscaled URL: " . $result['url'] . "\n";
        echo "📐 New dimensions: " . $result['dimensions'] . "\n";
        echo "🔍 Scale: " . $result['scale'] . "x\n";
        return $result;
    } else {
        echo "❌ Error: HTTP $httpCode\n";
        echo $response . "\n";
        return null;
    }
}

// Простое увеличение в 2 раза
$result = upscaleImage('your-api-key', 123);

// Увеличение в 4 раза с улучшением лиц
$result = upscaleImage('your-api-key', 123, [
    'scale' => 4,
    'face_enhance' => true,
    'denoise' => 0.7
]);

Smart Upscale - Умное увеличение

💡 Что такое Smart Upscale? Smart Upscale - это функция автоматического увеличения разрешения изображения после генерации. Включена по умолчанию! При запросе размера больше 512px, система автоматически генерирует в половинном размере и масштабирует до целевого с коэффициентом 2x.

🚀 Преимущества Smart Upscale

  • Скорость - генерация в низком разрешении происходит в 2-4 раза быстрее
  • 💰 Экономия ресурсов - меньше потребление VRAM и вычислительных ресурсов
  • 🎯 Качество - финальное изображение получается высокого качества благодаря RealESRGAN
  • 🔄 Автоматизация - весь процесс происходит автоматически в одном запросе

Как работает Smart Upscale

🔄 Автоматическое поведение (по умолчанию включено)

// Запрос размера 1024x1024 - автоматически применится smart upscale
{
    "prompt": "ultra realistic portrait, 8k quality",
    "width": 1024,             // Целевой размер
    "height": 1024,            // Автоматически: генерация 512x512 → upscale до 1024x1024
    "steps": 30
    // smart_upscale: true по умолчанию
}

// Для размеров ≤512px smart upscale автоматически отключается
{
    "prompt": "icon design",
    "width": 256,              // Маленький размер
    "height": 256,             // Генерируется напрямую без upscale
    "steps": 20
}

// Явное отключение smart upscale
{
    "prompt": "detailed artwork",
    "width": 1024,
    "height": 1024,
    "smart_upscale": false,    // Генерировать напрямую в 1024x1024 (медленнее)
    "steps": 30
}

🖼️ В эндпоинте /api/img2img

const formData = new FormData();
formData.append('image', imageFile);
formData.append('prompt', 'enhance quality, ultra HD');
formData.append('width', '512');          // Трансформируем в низком разрешении
formData.append('height', '512');
formData.append('smart_upscale', 'true'); // Включаем Smart Upscale
formData.append('target_width', '2048');  // Целевой размер
formData.append('target_height', '2048');

Рекомендации по использованию

✅ Когда использовать Smart Upscale

  • Нужны изображения высокого разрешения (2K, 4K)
  • Важна скорость генерации
  • Ограничены ресурсы GPU
  • Генерация сложных сцен с множеством деталей

⚠️ Ограничения

  • Не рекомендуется для изображений с текстом - может исказить буквы
  • Максимальный коэффициент увеличения - 4x
  • Требует дополнительное время на upscale (5-15 секунд)
  • Занимает больше места на диске (финальное изображение больше)

Примеры оптимальных настроек

Сценарий Исходный размер Целевой размер Рекомендации
Портреты 512×512 2048×2048 Включить face_enhance для лучшего качества лиц
Пейзажи 768×512 3072×2048 Использовать denoise=0.3 для сохранения деталей
Архитектура 512×768 2048×3072 Увеличить steps до 40-50 для лучшей детализации
Быстрые превью 256×256 1024×1024 Использовать model="flux-schnell" для скорости

Получение статуса изображения

GET /api/get/<id>

Быстрая проверка статуса генерации изображения по ID. Упрощенный эндпоинт для получения основной информации.

Параметры

Параметр Тип Описание
id required integer ID изображения, полученный при создании запроса на генерацию

Ответ

{
    "status": "completed",  // "generating", "completed", "failed", "queued"
    "url": "/uploads/images/flux_20250902_193838_c5dcc7ee.png",  // URL готового изображения (если completed)
    "message": "Изображение успешно сгенерировано"  // Сообщение о статусе
}

Статусы генерации

Статус Описание Действие
queued Запрос в очереди на обработку Продолжайте проверять статус
generating Изображение генерируется Продолжайте проверять статус
completed Генерация завершена успешно Используйте URL для получения изображения
failed Ошибка при генерации Проверьте сообщение об ошибке

Примеры использования

import requests
import time

# Запуск генерации
response = requests.post(
    "https://image.nalitek.com/api/generate",
    headers={"X-API-Key": "your_api_key"},
    json={"prompt": "Beautiful sunset", "width": 512, "height": 512}
)
image_id = response.json()["id"]

# Проверка статуса
while True:
    status = requests.get(
        f"https://image.nalitek.com/api/get/{image_id}",
        headers={"X-API-Key": "your_api_key"}
    ).json()
    
    print(f"Status: {status['status']}")
    
    if status["status"] == "completed":
        print(f"Image URL: {status['url']}")
        break
    elif status["status"] == "failed":
        print(f"Error: {status['message']}")
        break
    
    time.sleep(2)  # Ждем 2 секунды перед следующей проверкой
async function checkImageStatus(imageId) {
    const response = await fetch(
        `https://image.nalitek.com/api/get/${imageId}`,
        {
            headers: {
                'X-API-Key': 'your_api_key'
            }
        }
    );
    
    const status = await response.json();
    console.log(`Status: ${status.status}`);
    
    if (status.status === 'completed') {
        console.log(`Image URL: ${status.url}`);
        return status.url;
    } else if (status.status === 'failed') {
        throw new Error(status.message);
    }
    
    // Повторить через 2 секунды
    await new Promise(resolve => setTimeout(resolve, 2000));
    return checkImageStatus(imageId);
}
# Проверка статуса изображения с ID 123
curl -X GET \
  https://image.nalitek.com/api/get/123 \
  -H "X-API-Key: your_api_key"

# Ответ для завершенной генерации:
# {
#   "status": "completed",
#   "url": "/uploads/images/flux_20250902_193838_c5dcc7ee.png",
#   "message": "Изображение успешно сгенерировано"
# }
💡 Совет: Этот эндпоинт оптимизирован для быстрых проверок статуса. Для получения полной информации об изображении используйте /api/images/{id}.

Проверка статуса

GET /api/status

Получение общего статуса API и доступности сервисов.

Ответ

{
    "status": "operational",
    "version": "2.0",
    "services": {
        "generation": "operational",
        "img2img": "operational",
        "upscale": "operational",
        "storage": "operational",
        "websocket": "operational"
    },
    "models": {
        "flux-dev": {
            "status": "available",
            "queue_size": 5,
            "avg_generation_time": 28.5
        },
        "flux-schnell": {
            "status": "available",
            "queue_size": 2,
            "avg_generation_time": 15.3
        }
    },
    "timestamp": "2025-01-15T10:30:00Z"
}

Примеры использования

import requests

def check_api_status():
    """Проверка статуса API"""
    url = "https://image.nalitek.com/api/status"

    try:
        response = requests.get(url, timeout=5)

        if response.status_code == 200:
            status = response.json()
            print(f"✅ API Status: {status['status']}")
            print(f"📦 Version: {status['version']}")

            # Проверка сервисов
            for service, state in status['services'].items():
                icon = "✅" if state == "operational" else "❌"
                print(f"{icon} {service}: {state}")

            # Проверка моделей
            print("\n📊 Models:")
            for model, info in status['models'].items():
                print(f"  {model}:")
                print(f"    Status: {info['status']}")
                print(f"    Queue: {info['queue_size']} tasks")
                print(f"    Avg time: {info['avg_generation_time']}s")

            return status
        else:
            print(f"❌ API returned status code: {response.status_code}")
            return None
    except requests.exceptions.RequestException as e:
        print(f"❌ Failed to reach API: {e}")
        return None

# Использование
status = check_api_status()

# Проверка перед началом работы
if status and status['status'] == 'operational':
    print("\n✅ API is ready for requests!")
else:
    print("\n⚠️ API may be experiencing issues")
async function checkApiStatus() {
    const url = 'https://image.nalitek.com/api/status';

    try {
        const response = await fetch(url);

        if (response.status === 200) {
            const status = await response.json();
            console.log(`✅ API Status: ${status.status}`);
            console.log(`📦 Version: ${status.version}`);

            // Проверка сервисов
            for (const [service, state] of Object.entries(status.services)) {
                const icon = state === 'operational' ? '✅' : '❌';
                console.log(`${icon} ${service}: ${state}`);
            }

            // Проверка моделей
            console.log('\n📊 Models:');
            for (const [model, info] of Object.entries(status.models)) {
                console.log(`  ${model}:`);
                console.log(`    Status: ${info.status}`);
                console.log(`    Queue: ${info.queue_size} tasks`);
                console.log(`    Avg time: ${info.avg_generation_time}s`);
            }

            return status;
        } else {
            console.error(`❌ API returned status code: ${response.status}`);
            return null;
        }
    } catch (error) {
        console.error(`❌ Failed to reach API: ${error}`);
        return null;
    }
}

// Использование
checkApiStatus().then(status => {
    if (status && status.status === 'operational') {
        console.log('\n✅ API is ready for requests!');
    } else {
        console.log('\n⚠️ API may be experiencing issues');
    }
});
<?php

function checkApiStatus() {
    $url = 'https://image.nalitek.com/api/status';

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 5);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($httpCode === 200) {
        $status = json_decode($response, true);
        echo "✅ API Status: " . $status['status'] . "\n";
        echo "📦 Version: " . $status['version'] . "\n";

        // Проверка сервисов
        foreach ($status['services'] as $service => $state) {
            $icon = $state === 'operational' ? '✅' : '❌';
            echo "$icon $service: $state\n";
        }

        // Проверка моделей
        echo "\n📊 Models:\n";
        foreach ($status['models'] as $model => $info) {
            echo "  $model:\n";
            echo "    Status: " . $info['status'] . "\n";
            echo "    Queue: " . $info['queue_size'] . " tasks\n";
            echo "    Avg time: " . $info['avg_generation_time'] . "s\n";
        }

        return $status;
    } else {
        echo "❌ API returned status code: $httpCode\n";
        return null;
    }
}

// Использование
$status = checkApiStatus();

// Проверка перед началом работы
if ($status && $status['status'] === 'operational') {
    echo "\n✅ API is ready for requests!\n";
} else {
    echo "\n⚠️ API may be experiencing issues\n";
}

Управление изображениями

GET /api/images

Получение списка изображений пользователя с пагинацией и фильтрами.

Параметры запроса

Параметр Тип Описание По умолчанию
page integer Номер страницы 1
per_page integer Элементов на странице (1-100) 20
status string Фильтр по статусу (generating/completed/failed) -
is_upscaled boolean Фильтр по upscale статусу -
sort string Сортировка (date_desc/date_asc) date_desc

Примеры использования

# Получить список изображений
curl -X GET "https://image.nalitek.com/api/images?page=1&per_page=20" \
  -H "X-API-Key: your-api-key"

# С фильтрами
curl -X GET "https://image.nalitek.com/api/images?status=completed&sort=date_desc&per_page=50" \
  -H "X-API-Key: your-api-key"

# Только upscaled изображения
curl -X GET "https://image.nalitek.com/api/images?is_upscaled=true" \
  -H "X-API-Key: your-api-key"

# Получить детали изображения
curl -X GET https://image.nalitek.com/api/images/4738 \
  -H "X-API-Key: your-api-key"

# Удалить изображение
curl -X DELETE https://image.nalitek.com/api/images/4738 \
  -H "X-API-Key: your-api-key"

# Скачать изображение
curl -X GET https://image.nalitek.com/api/images/4738 \
  -H "X-API-Key: your-api-key" \
  | jq -r '.url' \
  | xargs curl -o image.png

# Получить все изображения с пагинацией (bash скрипт)
#!/bin/bash
API_KEY="your-api-key"
PAGE=1

while true; do
  RESPONSE=$(curl -s -X GET "https://image.nalitek.com/api/images?page=$PAGE&per_page=100" \
    -H "X-API-Key: $API_KEY")
  
  # Извлечь изображения
  echo "$RESPONSE" | jq '.images[]'
  
  # Проверить, есть ли еще страницы
  TOTAL_PAGES=$(echo "$RESPONSE" | jq '.pages')
  
  if [ "$PAGE" -ge "$TOTAL_PAGES" ]; then
    break
  fi
  
  PAGE=$((PAGE + 1))
done
import requests

class ImageManager:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://image.nalitek.com"
        self.headers = {"X-API-Key": api_key}

    def get_images(self, page=1, per_page=20, **filters):
        """Получение списка изображений"""
        url = f"{self.base_url}/api/images"

        params = {
            'page': page,
            'per_page': per_page
        }

        # Добавление фильтров
        if 'status' in filters:
            params['status'] = filters['status']
        if 'is_upscaled' in filters:
            params['is_upscaled'] = filters['is_upscaled']
        if 'sort' in filters:
            params['sort'] = filters['sort']

        response = requests.get(url, params=params, headers=self.headers)

        if response.status_code == 200:
            data = response.json()
            print(f"📸 Found {data['total']} images")
            print(f"📄 Page {data['current_page']} of {data['total_pages']}")

            for image in data['images']:
                print(f"\n🆔 ID: {image['id']}")
                print(f"📝 Prompt: {image['prompt'][:50]}...")
                print(f"📐 Size: {image['width']}x{image['height']}")
                print(f"✅ Status: {image['status']}")

            return data
        else:
            print(f"❌ Error: {response.status_code}")
            return None

    def get_image_details(self, image_id):
        """Получение деталей изображения"""
        url = f"{self.base_url}/api/images/{image_id}"
        response = requests.get(url, headers=self.headers)

        if response.status_code == 200:
            image = response.json()
            print(f"🆔 Image ID: {image['id']}")
            print(f"📝 Prompt: {image['prompt']}")
            print(f"🔗 URL: {image['url']}")
            print(f"📐 Dimensions: {image['width']}x{image['height']}")
            print(f"🌱 Seed: {image['seed']}")
            print(f"⏱️ Generation time: {image['generation_time']}s")
            return image
        else:
            print(f"❌ Error: {response.status_code}")
            return None

    def delete_image(self, image_id):
        """Удаление изображения"""
        url = f"{self.base_url}/api/images/{image_id}"
        response = requests.delete(url, headers=self.headers)

        if response.status_code == 204:
            print(f"✅ Image {image_id} deleted successfully")
            return True
        else:
            print(f"❌ Error: {response.status_code}")
            return False

    def get_all_images(self):
        """Получение всех изображений (с пагинацией)"""
        all_images = []
        page = 1

        while True:
            data = self.get_images(page=page, per_page=100)
            if not data:
                break

            all_images.extend(data['images'])

            if page >= data['total_pages']:
                break

            page += 1

        print(f"\n📊 Total images collected: {len(all_images)}")
        return all_images

# Использование
manager = ImageManager("your-api-key")

# Получить последние изображения
manager.get_images(page=1, per_page=10)

# Получить только завершенные изображения
manager.get_images(status='completed', sort='date_desc')

# Получить детали конкретного изображения
manager.get_image_details(4738)

# Удалить изображение
manager.delete_image(4739)

# Получить все изображения
all_images = manager.get_all_images()
class ImageManager {
    constructor(apiKey) {
        this.apiKey = apiKey;
        this.baseUrl = 'https://image.nalitek.com';
    }

    async getImages(page = 1, perPage = 20, filters = {}) {
        const url = new URL(`${this.baseUrl}/api/images`);

        // Добавление параметров
        url.searchParams.append('page', page);
        url.searchParams.append('per_page', perPage);

        // Добавление фильтров
        if (filters.status) url.searchParams.append('status', filters.status);
        if (filters.is_upscaled !== undefined) {
            url.searchParams.append('is_upscaled', filters.is_upscaled);
        }
        if (filters.sort) url.searchParams.append('sort', filters.sort);

        try {
            const response = await fetch(url, {
                headers: { 'X-API-Key': this.apiKey }
            });

            if (response.status === 200) {
                const data = await response.json();
                console.log(`📸 Found ${data.total} images`);
                console.log(`📄 Page ${data.current_page} of ${data.total_pages}`);

                data.images.forEach(image => {
                    console.log(`\n🆔 ID: ${image.id}`);
                    console.log(`📝 Prompt: ${image.prompt.substring(0, 50)}...`);
                    console.log(`📐 Size: ${image.width}x${image.height}`);
                    console.log(`✅ Status: ${image.status}`);
                });

                return data;
            } else {
                console.error(`❌ Error: ${response.status}`);
                return null;
            }
        } catch (error) {
            console.error('Request failed:', error);
            return null;
        }
    }

    async getImageDetails(imageId) {
        const url = `${this.baseUrl}/api/images/${imageId}`;

        try {
            const response = await fetch(url, {
                headers: { 'X-API-Key': this.apiKey }
            });

            if (response.status === 200) {
                const image = await response.json();
                console.log(`🆔 Image ID: ${image.id}`);
                console.log(`📝 Prompt: ${image.prompt}`);
                console.log(`🔗 URL: ${image.url}`);
                console.log(`📐 Dimensions: ${image.width}x${image.height}`);
                console.log(`🌱 Seed: ${image.seed}`);
                console.log(`⏱️ Generation time: ${image.generation_time}s`);
                return image;
            } else {
                console.error(`❌ Error: ${response.status}`);
                return null;
            }
        } catch (error) {
            console.error('Request failed:', error);
            return null;
        }
    }

    async deleteImage(imageId) {
        const url = `${this.baseUrl}/api/images/${imageId}`;

        try {
            const response = await fetch(url, {
                method: 'DELETE',
                headers: { 'X-API-Key': this.apiKey }
            });

            if (response.status === 204) {
                console.log(`✅ Image ${imageId} deleted successfully`);
                return true;
            } else {
                console.error(`❌ Error: ${response.status}`);
                return false;
            }
        } catch (error) {
            console.error('Request failed:', error);
            return false;
        }
    }

    async getAllImages() {
        const allImages = [];
        let page = 1;
        let totalPages = 1;

        while (page <= totalPages) {
            const data = await this.getImages(page, 100);
            if (!data) break;

            allImages.push(...data.images);
            totalPages = data.total_pages;
            page++;
        }

        console.log(`\n📊 Total images collected: ${allImages.length}`);
        return allImages;
    }
}

// Использование
const manager = new ImageManager('your-api-key');

// Получить последние изображения
manager.getImages(1, 10);

// Получить только завершенные изображения
manager.getImages(1, 20, { status: 'completed', sort: 'date_desc' });

// Получить детали конкретного изображения
manager.getImageDetails(123);

// Удалить изображение
manager.deleteImage(456);

// Получить все изображения
manager.getAllImages();
<?php

class ImageManager {
    private $apiKey;
    private $baseUrl = 'https://image.nalitek.com';

    public function __construct($apiKey) {
        $this->apiKey = $apiKey;
    }

    public function getImages($page = 1, $perPage = 20, $filters = []) {
        $url = $this->baseUrl . '/api/images';

        $params = [
            'page' => $page,
            'per_page' => $perPage
        ];

        // Добавление фильтров
        if (isset($filters['status'])) {
            $params['status'] = $filters['status'];
        }
        if (isset($filters['is_upscaled'])) {
            $params['is_upscaled'] = $filters['is_upscaled'];
        }
        if (isset($filters['sort'])) {
            $params['sort'] = $filters['sort'];
        }

        $url .= '?' . http_build_query($params);

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $this->apiKey]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 200) {
            $data = json_decode($response, true);
            echo "📸 Found {$data['total']} images\n";
            echo "📄 Page {$data['current_page']} of {$data['total_pages']}\n";

            foreach ($data['images'] as $image) {
                echo "\n🆔 ID: {$image['id']}\n";
                echo "📝 Prompt: " . substr($image['prompt'], 0, 50) . "...\n";
                echo "📐 Size: {$image['width']}x{$image['height']}\n";
                echo "✅ Status: {$image['status']}\n";
            }

            return $data;
        } else {
            echo "❌ Error: HTTP $httpCode\n";
            return null;
        }
    }

    public function getImageDetails($imageId) {
        $url = $this->baseUrl . "/api/images/$imageId";

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $this->apiKey]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 200) {
            $image = json_decode($response, true);
            echo "🆔 Image ID: {$image['id']}\n";
            echo "📝 Prompt: {$image['prompt']}\n";
            echo "🔗 URL: {$image['url']}\n";
            echo "📐 Dimensions: {$image['width']}x{$image['height']}\n";
            echo "🌱 Seed: {$image['seed']}\n";
            echo "⏱️ Generation time: {$image['generation_time']}s\n";
            return $image;
        } else {
            echo "❌ Error: HTTP $httpCode\n";
            return null;
        }
    }

    public function deleteImage($imageId) {
        $url = $this->baseUrl . "/api/images/$imageId";

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $this->apiKey]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 204) {
            echo "✅ Image $imageId deleted successfully\n";
            return true;
        } else {
            echo "❌ Error: HTTP $httpCode\n";
            return false;
        }
    }
}

// Использование
$manager = new ImageManager('your-api-key');

// Получить последние изображения
$manager->getImages(1, 10);

// Получить только завершенные изображения
$manager->getImages(1, 20, ['status' => 'completed', 'sort' => 'date_desc']);

// Получить детали конкретного изображения
$manager->getImageDetails(123);

// Удалить изображение
$manager->deleteImage(456);

User Endpoints

GET /api/user/profile

Получение профиля текущего пользователя.

Ответ

{
    "id": 347,
    "username": "alex_petrov",
    "email": "alex.petrov@gmail.com",
    "role": "user",
    "plan": "pro",
    "created_at": "2024-11-15T14:22:18Z",
    "avatar_url": "https://image.nalitek.com/uploads/avatars/avatar_347_1731678138.jpg",
    "settings": {
        "default_model": "flux-dev",
        "default_quality": "high",
        "email_notifications": true,
        "webhook_enabled": false
    }
}
PUT /api/user/profile

Обновление профиля пользователя.

Примеры использования

import requests

class UserAPI:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://image.nalitek.com"
        self.headers = {"X-API-Key": api_key, "Content-Type": "application/json"}

    def get_profile(self):
        """Получить профиль пользователя"""
        url = f"{self.base_url}/api/user/profile"
        response = requests.get(url, headers=self.headers)

        if response.status_code == 200:
            profile = response.json()
            print(f"👤 User: {profile['username']}")
            print(f"📧 Email: {profile['email']}")
            print(f"🎯 Plan: {profile['plan']}")
            print(f"🎨 Default model: {profile['settings']['default_model']}")
            return profile
        else:
            print(f"❌ Error: {response.status_code}")
            return None

    def update_profile(self, updates):
        """Обновить профиль пользователя"""
        url = f"{self.base_url}/api/user/profile"
        response = requests.put(url, json=updates, headers=self.headers)

        if response.status_code == 200:
            profile = response.json()
            print("✅ Profile updated successfully")
            return profile
        else:
            print(f"❌ Error: {response.status_code}")
            return None

    def update_settings(self, settings):
        """Обновить настройки пользователя"""
        return self.update_profile({"settings": settings})

    def change_password(self, current_password, new_password):
        """Изменить пароль"""
        url = f"{self.base_url}/api/user/password"
        data = {
            "current_password": current_password,
            "new_password": new_password
        }
        response = requests.post(url, json=data, headers=self.headers)

        if response.status_code == 200:
            print("✅ Password changed successfully")
            return True
        else:
            print(f"❌ Error: {response.status_code}")
            return False

# Использование
user_api = UserAPI("your-api-key")

# Получить профиль
profile = user_api.get_profile()

# Обновить настройки
user_api.update_settings({
    "default_model": "flux-schnell",
    "default_quality": "ultra",
    "email_notifications": False
})

# Изменить пароль
user_api.change_password("old_password", "new_secure_password")
class UserAPI {
    constructor(apiKey) {
        this.apiKey = apiKey;
        this.baseUrl = 'https://image.nalitek.com';
    }

    async getProfile() {
        const url = `${this.baseUrl}/api/user/profile`;

        try {
            const response = await fetch(url, {
                headers: { 'X-API-Key': this.apiKey }
            });

            if (response.status === 200) {
                const profile = await response.json();
                console.log(`👤 User: ${profile.username}`);
                console.log(`📧 Email: ${profile.email}`);
                console.log(`🎯 Plan: ${profile.plan}`);
                console.log(`🎨 Default model: ${profile.settings.default_model}`);
                return profile;
            } else {
                console.error(`❌ Error: ${response.status}`);
                return null;
            }
        } catch (error) {
            console.error('Request failed:', error);
            return null;
        }
    }

    async updateProfile(updates) {
        const url = `${this.baseUrl}/api/user/profile`;

        try {
            const response = await fetch(url, {
                method: 'PUT',
                headers: {
                    'X-API-Key': this.apiKey,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(updates)
            });

            if (response.status === 200) {
                const profile = await response.json();
                console.log('✅ Profile updated successfully');
                return profile;
            } else {
                console.error(`❌ Error: ${response.status}`);
                return null;
            }
        } catch (error) {
            console.error('Request failed:', error);
            return null;
        }
    }

    async updateSettings(settings) {
        return this.updateProfile({ settings });
    }

    async changePassword(currentPassword, newPassword) {
        const url = `${this.baseUrl}/api/user/password`;

        try {
            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'X-API-Key': this.apiKey,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    current_password: currentPassword,
                    new_password: newPassword
                })
            });

            if (response.status === 200) {
                console.log('✅ Password changed successfully');
                return true;
            } else {
                console.error(`❌ Error: ${response.status}`);
                return false;
            }
        } catch (error) {
            console.error('Request failed:', error);
            return false;
        }
    }
}

// Использование
const userApi = new UserAPI('your-api-key');

// Получить профиль
userApi.getProfile();

// Обновить настройки
userApi.updateSettings({
    default_model: 'flux-schnell',
    default_quality: 'ultra',
    email_notifications: false
});

// Изменить пароль
userApi.changePassword('old_password', 'new_secure_password');
<?php

class UserAPI {
    private $apiKey;
    private $baseUrl = 'https://image.nalitek.com';

    public function __construct($apiKey) {
        $this->apiKey = $apiKey;
    }

    public function getProfile() {
        $url = $this->baseUrl . '/api/user/profile';

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $this->apiKey]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 200) {
            $profile = json_decode($response, true);
            echo "👤 User: {$profile['username']}\n";
            echo "📧 Email: {$profile['email']}\n";
            echo "🎯 Plan: {$profile['plan']}\n";
            echo "🎨 Default model: {$profile['settings']['default_model']}\n";
            return $profile;
        } else {
            echo "❌ Error: HTTP $httpCode\n";
            return null;
        }
    }

    public function updateProfile($updates) {
        $url = $this->baseUrl . '/api/user/profile';

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($updates));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'X-API-Key: ' . $this->apiKey,
            'Content-Type: application/json'
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 200) {
            echo "✅ Profile updated successfully\n";
            return json_decode($response, true);
        } else {
            echo "❌ Error: HTTP $httpCode\n";
            return null;
        }
    }

    public function updateSettings($settings) {
        return $this->updateProfile(['settings' => $settings]);
    }

    public function changePassword($currentPassword, $newPassword) {
        $url = $this->baseUrl . '/api/user/password';

        $data = [
            'current_password' => $currentPassword,
            'new_password' => $newPassword
        ];

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'X-API-Key: ' . $this->apiKey,
            'Content-Type: application/json'
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 200) {
            echo "✅ Password changed successfully\n";
            return true;
        } else {
            echo "❌ Error: HTTP $httpCode\n";
            return false;
        }
    }
}

// Использование
$userApi = new UserAPI('your-api-key');

// Получить профиль
$profile = $userApi->getProfile();

// Обновить настройки
$userApi->updateSettings([
    'default_model' => 'flux-schnell',
    'default_quality' => 'ultra',
    'email_notifications' => false
]);

// Изменить пароль
$userApi->changePassword('old_password', 'new_secure_password');

Управление API ключами

GET /api/user/api-keys

Получение списка API ключей пользователя.

POST /api/user/api-keys

Создание нового API ключа.

DELETE /api/user/api-keys/{key_id}

Удаление API ключа.

Примеры использования

import requests

class APIKeyManager:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://image.nalitek.com"
        self.headers = {"X-API-Key": api_key, "Content-Type": "application/json"}

    def list_keys(self):
        """Получить список API ключей"""
        url = f"{self.base_url}/api/user/api-keys"
        response = requests.get(url, headers=self.headers)

        if response.status_code == 200:
            keys = response.json()
            print(f"🔑 Total API keys: {len(keys['api_keys'])}")

            for key in keys['api_keys']:
                print(f"\n🆔 ID: {key['id']}")
                print(f"📝 Name: {key['name']}")
                print(f"🔑 Prefix: {key['prefix']}")
                print(f"📅 Created: {key['created_at']}")
                print(f"⏰ Last used: {key['last_used_at']}")
                print(f"📊 Usage: {key['usage']['total_requests']} requests")

            return keys
        else:
            print(f"❌ Error: {response.status_code}")
            return None

    def create_key(self, name, ip_whitelist=None, rate_limit=None):
        """Создать новый API ключ"""
        url = f"{self.base_url}/api/user/api-keys"

        data = {"name": name}
        if ip_whitelist:
            data["ip_whitelist"] = ip_whitelist
        if rate_limit:
            data["rate_limit"] = rate_limit

        response = requests.post(url, json=data, headers=self.headers)

        if response.status_code == 201:
            key_data = response.json()
            print(f"✅ API key created successfully")
            print(f"🔑 Key: {key_data['key']}")
            print(f"⚠️  Save this key! It won't be shown again.")
            return key_data
        else:
            print(f"❌ Error: {response.status_code}")
            return None

    def delete_key(self, key_id):
        """Удалить API ключ"""
        url = f"{self.base_url}/api/user/api-keys/{key_id}"
        response = requests.delete(url, headers=self.headers)

        if response.status_code == 204:
            print(f"✅ API key {key_id} deleted successfully")
            return True
        else:
            print(f"❌ Error: {response.status_code}")
            return False

    def rotate_key(self, key_id, name=None):
        """Ротация API ключа (удаление старого и создание нового)"""
        # Удаляем старый ключ
        if self.delete_key(key_id):
            # Создаем новый ключ
            new_name = name or f"Rotated key (from {key_id})"
            return self.create_key(new_name)
        return None

# Использование
manager = APIKeyManager("your-api-key")

# Список ключей
manager.list_keys()

# Создать новый ключ
new_key = manager.create_key(
    name="Production API Key",
    ip_whitelist=["192.168.1.1", "10.0.0.1"],
    rate_limit=1000
)

# Удалить ключ
manager.delete_key(123)

# Ротация ключа
manager.rotate_key(456, "New Production Key")
class APIKeyManager {
    constructor(apiKey) {
        this.apiKey = apiKey;
        this.baseUrl = 'https://image.nalitek.com';
    }

    async listKeys() {
        const url = `${this.baseUrl}/api/user/api-keys`;

        try {
            const response = await fetch(url, {
                headers: { 'X-API-Key': this.apiKey }
            });

            if (response.status === 200) {
                const keys = await response.json();
                console.log(`🔑 Total API keys: ${keys.api_keys.length}`);

                keys.api_keys.forEach(key => {
                    console.log(`\n🆔 ID: ${key.id}`);
                    console.log(`📝 Name: ${key.name}`);
                    console.log(`🔑 Prefix: ${key.prefix}`);
                    console.log(`📅 Created: ${key.created_at}`);
                    console.log(`⏰ Last used: ${key.last_used_at}`);
                    console.log(`📊 Usage: ${key.usage.total_requests} requests`);
                });

                return keys;
            } else {
                console.error(`❌ Error: ${response.status}`);
                return null;
            }
        } catch (error) {
            console.error('Request failed:', error);
            return null;
        }
    }

    async createKey(name, ipWhitelist = null, rateLimit = null) {
        const url = `${this.baseUrl}/api/user/api-keys`;

        const data = { name };
        if (ipWhitelist) data.ip_whitelist = ipWhitelist;
        if (rateLimit) data.rate_limit = rateLimit;

        try {
            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'X-API-Key': this.apiKey,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(data)
            });

            if (response.status === 201) {
                const keyData = await response.json();
                console.log('✅ API key created successfully');
                console.log(`🔑 Key: ${keyData.key}`);
                console.log('⚠️  Save this key! It won\'t be shown again.');
                return keyData;
            } else {
                console.error(`❌ Error: ${response.status}`);
                return null;
            }
        } catch (error) {
            console.error('Request failed:', error);
            return null;
        }
    }

    async deleteKey(keyId) {
        const url = `${this.baseUrl}/api/user/api-keys/${keyId}`;

        try {
            const response = await fetch(url, {
                method: 'DELETE',
                headers: { 'X-API-Key': this.apiKey }
            });

            if (response.status === 204) {
                console.log(`✅ API key ${keyId} deleted successfully`);
                return true;
            } else {
                console.error(`❌ Error: ${response.status}`);
                return false;
            }
        } catch (error) {
            console.error('Request failed:', error);
            return false;
        }
    }
}

// Использование
const manager = new APIKeyManager('your-api-key');

// Список ключей
manager.listKeys();

// Создать новый ключ
manager.createKey(
    'Production API Key',
    ['192.168.1.1', '10.0.0.1'],
    1000
);

// Удалить ключ
manager.deleteKey(123);
<?php

class APIKeyManager {
    private $apiKey;
    private $baseUrl = 'https://image.nalitek.com';

    public function __construct($apiKey) {
        $this->apiKey = $apiKey;
    }

    public function listKeys() {
        $url = $this->baseUrl . '/api/user/api-keys';

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $this->apiKey]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 200) {
            $keys = json_decode($response, true);
            echo "🔑 Total API keys: " . count($keys['api_keys']) . "\n";

            foreach ($keys['api_keys'] as $key) {
                echo "\n🆔 ID: {$key['id']}\n";
                echo "📝 Name: {$key['name']}\n";
                echo "🔑 Prefix: {$key['prefix']}\n";
                echo "📅 Created: {$key['created_at']}\n";
                echo "⏰ Last used: {$key['last_used_at']}\n";
                echo "📊 Usage: {$key['usage']['total_requests']} requests\n";
            }

            return $keys;
        } else {
            echo "❌ Error: HTTP $httpCode\n";
            return null;
        }
    }

    public function createKey($name, $ipWhitelist = null, $rateLimit = null) {
        $url = $this->baseUrl . '/api/user/api-keys';

        $data = ['name' => $name];
        if ($ipWhitelist) $data['ip_whitelist'] = $ipWhitelist;
        if ($rateLimit) $data['rate_limit'] = $rateLimit;

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'X-API-Key: ' . $this->apiKey,
            'Content-Type: application/json'
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 201) {
            $keyData = json_decode($response, true);
            echo "✅ API key created successfully\n";
            echo "🔑 Key: {$keyData['key']}\n";
            echo "⚠️  Save this key! It won't be shown again.\n";
            return $keyData;
        } else {
            echo "❌ Error: HTTP $httpCode\n";
            return null;
        }
    }

    public function deleteKey($keyId) {
        $url = $this->baseUrl . "/api/user/api-keys/$keyId";

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $this->apiKey]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 204) {
            echo "✅ API key $keyId deleted successfully\n";
            return true;
        } else {
            echo "❌ Error: HTTP $httpCode\n";
            return false;
        }
    }
}

// Использование
$manager = new APIKeyManager('your-api-key');

// Список ключей
$manager->listKeys();

// Создать новый ключ
$manager->createKey(
    'Production API Key',
    ['192.168.1.1', '10.0.0.1'],
    1000
);

// Удалить ключ
$manager->deleteKey(123);

Batch Processing

POST /api/batch/generate

Массовая генерация изображений (до 10 за раз).

Параметры запроса

Параметр Тип Описание
batch required array Массив объектов с параметрами генерации (max 10)
webhook_url optional string URL для уведомлений о завершении

Примеры использования

import requests
import time

class BatchProcessor:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://image.nalitek.com"
        self.headers = {"X-API-Key": api_key, "Content-Type": "application/json"}

    def batch_generate(self, prompts, common_params=None):
        """Массовая генерация изображений"""
        url = f"{self.base_url}/api/batch/generate"

        # Подготовка batch данных
        batch = []
        for prompt in prompts:
            item = {"prompt": prompt}
            if common_params:
                item.update(common_params)
            batch.append(item)

        data = {
            "batch": batch,
            "webhook_url": "https://your-webhook.com/batch-complete"
        }

        response = requests.post(url, json=data, headers=self.headers)

        if response.status_code == 201:
            result = response.json()
            print(f"✅ Batch created: {result['batch_id']}")
            print(f"📊 Total jobs: {result['total_jobs']}")
            print(f"⏱️ Estimated time: {result['estimated_time']}s")
            return result
        else:
            print(f"❌ Error: {response.status_code}")
            return None

    def check_batch_status(self, batch_id):
        """Проверка статуса batch"""
        url = f"{self.base_url}/api/batch/{batch_id}"
        response = requests.get(url, headers=self.headers)

        if response.status_code == 200:
            status = response.json()
            print(f"📊 Batch {batch_id}:")
            print(f"  Completed: {status['completed']}/{status['total']}")
            print(f"  Failed: {status['failed']}")
            print(f"  Status: {status['status']}")
            return status
        return None

    def wait_for_batch(self, batch_id, timeout=600):
        """Ожидание завершения batch"""
        start_time = time.time()

        while time.time() - start_time < timeout:
            status = self.check_batch_status(batch_id)

            if status and status['status'] == 'completed':
                print("✅ Batch completed!")
                return status
            elif status and status['status'] == 'failed':
                print("❌ Batch failed!")
                return status

            time.sleep(5)

        print(f"⏱️ Timeout after {timeout}s")
        return None

# Использование
processor = BatchProcessor("your-api-key")

# Создание batch с разными промптами
prompts = [
    "cyberpunk city at night",
    "beautiful japanese garden",
    "abstract art with vibrant colors",
    "portrait of a robot",
    "underwater coral reef"
]

batch = processor.batch_generate(
    prompts=prompts,
    common_params={
        "width": 1024,
        "height": 1024,
        "steps": 30,
        "model": "flux-dev"
    }
)

if batch:
    # Ожидание завершения
    result = processor.wait_for_batch(batch['batch_id'])

    if result:
        # Получение результатов
        for job in result['jobs']:
            print(f"\n🖼️ Image {job['id']}: {job['url']}")
class BatchProcessor {
    constructor(apiKey) {
        this.apiKey = apiKey;
        this.baseUrl = 'https://image.nalitek.com';
    }

    async batchGenerate(prompts, commonParams = {}) {
        const url = `${this.baseUrl}/api/batch/generate`;

        // Подготовка batch данных
        const batch = prompts.map(prompt => ({
            prompt,
            ...commonParams
        }));

        const data = {
            batch,
            webhook_url: 'https://your-webhook.com/batch-complete'
        };

        try {
            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'X-API-Key': this.apiKey,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(data)
            });

            if (response.status === 201) {
                const result = await response.json();
                console.log(`✅ Batch created: ${result.batch_id}`);
                console.log(`📊 Total jobs: ${result.total_jobs}`);
                console.log(`⏱️ Estimated time: ${result.estimated_time}s`);
                return result;
            } else {
                console.error(`❌ Error: ${response.status}`);
                return null;
            }
        } catch (error) {
            console.error('Request failed:', error);
            return null;
        }
    }

    async checkBatchStatus(batchId) {
        const url = `${this.baseUrl}/api/batch/${batchId}`;

        try {
            const response = await fetch(url, {
                headers: { 'X-API-Key': this.apiKey }
            });

            if (response.status === 200) {
                const status = await response.json();
                console.log(`📊 Batch ${batchId}:`);
                console.log(`  Completed: ${status.completed}/${status.total}`);
                console.log(`  Failed: ${status.failed}`);
                console.log(`  Status: ${status.status}`);
                return status;
            }
            return null;
        } catch (error) {
            console.error('Request failed:', error);
            return null;
        }
    }

    async waitForBatch(batchId, timeout = 600000) {
        const startTime = Date.now();

        while (Date.now() - startTime < timeout) {
            const status = await this.checkBatchStatus(batchId);

            if (status && status.status === 'completed') {
                console.log('✅ Batch completed!');
                return status;
            } else if (status && status.status === 'failed') {
                console.log('❌ Batch failed!');
                return status;
            }

            await new Promise(resolve => setTimeout(resolve, 5000));
        }

        console.log(`⏱️ Timeout after ${timeout}ms`);
        return null;
    }
}

// Использование
const processor = new BatchProcessor('your-api-key');

// Создание batch с разными промптами
const prompts = [
    'cyberpunk city at night',
    'beautiful japanese garden',
    'abstract art with vibrant colors',
    'portrait of a robot',
    'underwater coral reef'
];

async function runBatch() {
    const batch = await processor.batchGenerate(prompts, {
        width: 1024,
        height: 1024,
        steps: 30,
        model: 'flux-dev'
    });

    if (batch) {
        const result = await processor.waitForBatch(batch.batch_id);

        if (result) {
            result.jobs.forEach(job => {
                console.log(`\n🖼️ Image ${job.id}: ${job.url}`);
            });
        }
    }
}

runBatch();
<?php

class BatchProcessor {
    private $apiKey;
    private $baseUrl = 'https://image.nalitek.com';

    public function __construct($apiKey) {
        $this->apiKey = $apiKey;
    }

    public function batchGenerate($prompts, $commonParams = []) {
        $url = $this->baseUrl . '/api/batch/generate';

        // Подготовка batch данных
        $batch = [];
        foreach ($prompts as $prompt) {
            $item = array_merge(['prompt' => $prompt], $commonParams);
            $batch[] = $item;
        }

        $data = [
            'batch' => $batch,
            'webhook_url' => 'https://your-webhook.com/batch-complete'
        ];

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'X-API-Key: ' . $this->apiKey,
            'Content-Type: application/json'
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 201) {
            $result = json_decode($response, true);
            echo "✅ Batch created: {$result['batch_id']}\n";
            echo "📊 Total jobs: {$result['total_jobs']}\n";
            echo "⏱️ Estimated time: {$result['estimated_time']}s\n";
            return $result;
        } else {
            echo "❌ Error: HTTP $httpCode\n";
            return null;
        }
    }

    public function checkBatchStatus($batchId) {
        $url = $this->baseUrl . "/api/batch/$batchId";

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $this->apiKey]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 200) {
            $status = json_decode($response, true);
            echo "📊 Batch $batchId:\n";
            echo "  Completed: {$status['completed']}/{$status['total']}\n";
            echo "  Failed: {$status['failed']}\n";
            echo "  Status: {$status['status']}\n";
            return $status;
        }
        return null;
    }

    public function waitForBatch($batchId, $timeout = 600) {
        $startTime = time();

        while (time() - $startTime < $timeout) {
            $status = $this->checkBatchStatus($batchId);

            if ($status && $status['status'] === 'completed') {
                echo "✅ Batch completed!\n";
                return $status;
            } elseif ($status && $status['status'] === 'failed') {
                echo "❌ Batch failed!\n";
                return $status;
            }

            sleep(5);
        }

        echo "⏱️ Timeout after {$timeout}s\n";
        return null;
    }
}

// Использование
$processor = new BatchProcessor('your-api-key');

// Создание batch с разными промптами
$prompts = [
    'cyberpunk city at night',
    'beautiful japanese garden',
    'abstract art with vibrant colors',
    'portrait of a robot',
    'underwater coral reef'
];

$batch = $processor->batchGenerate($prompts, [
    'width' => 1024,
    'height' => 1024,
    'steps' => 30,
    'model' => 'flux-dev'
]);

if ($batch) {
    $result = $processor->waitForBatch($batch['batch_id']);

    if ($result) {
        foreach ($result['jobs'] as $job) {
            echo "\n🖼️ Image {$job['id']}: {$job['url']}\n";
        }
    }
}

Admin Panel

Внимание! Эти эндпоинты доступны только пользователям с ролью admin.
GET /api/admin/users

Получение списка всех пользователей.

GET /api/admin/statistics

Общая статистика системы.

POST /api/admin/user/{user_id}/quota

Изменение квот пользователя.

Примеры использования

import requests

class AdminAPI:
    def __init__(self, admin_api_key):
        self.api_key = admin_api_key
        self.base_url = "https://image.nalitek.com"
        self.headers = {"X-API-Key": admin_api_key, "Content-Type": "application/json"}

    def get_users(self, page=1, per_page=50):
        """Получить список пользователей"""
        url = f"{self.base_url}/api/admin/users"
        params = {"page": page, "per_page": per_page}

        response = requests.get(url, params=params, headers=self.headers)

        if response.status_code == 200:
            data = response.json()
            print(f"👥 Total users: {data['total']}")

            for user in data['users']:
                print(f"\n👤 {user['username']} ({user['email']})")
                print(f"   Plan: {user['plan']}")
                print(f"   Quota: {user['quota']['daily_generations']}/day")
                print(f"   Created: {user['created_at']}")

            return data
        else:
            print(f"❌ Error: {response.status_code}")
            return None

    def get_statistics(self):
        """Получить общую статистику"""
        url = f"{self.base_url}/api/admin/statistics"
        response = requests.get(url, headers=self.headers)

        if response.status_code == 200:
            stats = response.json()
            print("📊 System Statistics:")
            print(f"   Total users: {stats['total_users']}")
            print(f"   Active users (24h): {stats['active_users_24h']}")
            print(f"   Total generations: {stats['total_generations']}")
            print(f"   Generations today: {stats['generations_today']}")
            print(f"   Storage used: {stats['storage_used_gb']} GB")
            print(f"   Revenue this month: ${stats['revenue_month']}")
            return stats
        else:
            print(f"❌ Error: {response.status_code}")
            return None

    def update_user_quota(self, user_id, daily_generations, daily_upscales):
        """Обновить квоты пользователя"""
        url = f"{self.base_url}/api/admin/user/{user_id}/quota"
        data = {
            "daily_generations": daily_generations,
            "daily_upscales": daily_upscales
        }

        response = requests.post(url, json=data, headers=self.headers)

        if response.status_code == 200:
            print(f"✅ Quota updated for user {user_id}")
            return True
        else:
            print(f"❌ Error: {response.status_code}")
            return False

    def ban_user(self, user_id, reason):
        """Заблокировать пользователя"""
        url = f"{self.base_url}/api/admin/user/{user_id}/ban"
        data = {"reason": reason}

        response = requests.post(url, json=data, headers=self.headers)

        if response.status_code == 200:
            print(f"✅ User {user_id} banned")
            return True
        else:
            print(f"❌ Error: {response.status_code}")
            return False

# Использование
admin = AdminAPI("admin-api-key")

# Получить пользователей
admin.get_users(page=1)

# Получить статистику
admin.get_statistics()

# Обновить квоты
admin.update_user_quota(
    user_id=123,
    daily_generations=500,
    daily_upscales=100
)

# Заблокировать пользователя
admin.ban_user(456, "Violation of terms of service")
class AdminAPI {
    constructor(adminApiKey) {
        this.apiKey = adminApiKey;
        this.baseUrl = 'https://image.nalitek.com';
    }

    async getUsers(page = 1, perPage = 50) {
        const url = new URL(`${this.baseUrl}/api/admin/users`);
        url.searchParams.append('page', page);
        url.searchParams.append('per_page', perPage);

        try {
            const response = await fetch(url, {
                headers: { 'X-API-Key': this.apiKey }
            });

            if (response.status === 200) {
                const data = await response.json();
                console.log(`👥 Total users: ${data.total}`);

                data.users.forEach(user => {
                    console.log(`\n👤 ${user.username} (${user.email})`);
                    console.log(`   Plan: ${user.plan}`);
                    console.log(`   Quota: ${user.quota.daily_generations}/day`);
                    console.log(`   Created: ${user.created_at}`);
                });

                return data;
            } else {
                console.error(`❌ Error: ${response.status}`);
                return null;
            }
        } catch (error) {
            console.error('Request failed:', error);
            return null;
        }
    }

    async getStatistics() {
        const url = `${this.baseUrl}/api/admin/statistics`;

        try {
            const response = await fetch(url, {
                headers: { 'X-API-Key': this.apiKey }
            });

            if (response.status === 200) {
                const stats = await response.json();
                console.log('📊 System Statistics:');
                console.log(`   Total users: ${stats.total_users}`);
                console.log(`   Active users (24h): ${stats.active_users_24h}`);
                console.log(`   Total generations: ${stats.total_generations}`);
                console.log(`   Generations today: ${stats.generations_today}`);
                console.log(`   Storage used: ${stats.storage_used_gb} GB`);
                console.log(`   Revenue this month: ${stats.revenue_month}`);
                return stats;
            } else {
                console.error(`❌ Error: ${response.status}`);
                return null;
            }
        } catch (error) {
            console.error('Request failed:', error);
            return null;
        }
    }

    async updateUserQuota(userId, dailyGenerations, dailyUpscales) {
        const url = `${this.baseUrl}/api/admin/user/${userId}/quota`;

        try {
            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'X-API-Key': this.apiKey,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    daily_generations: dailyGenerations,
                    daily_upscales: dailyUpscales
                })
            });

            if (response.status === 200) {
                console.log(`✅ Quota updated for user ${userId}`);
                return true;
            } else {
                console.error(`❌ Error: ${response.status}`);
                return false;
            }
        } catch (error) {
            console.error('Request failed:', error);
            return false;
        }
    }
}

// Использование
const admin = new AdminAPI('admin-api-key');

// Получить пользователей
admin.getUsers(1);

// Получить статистику
admin.getStatistics();

// Обновить квоты
admin.updateUserQuota(123, 500, 100);
<?php

class AdminAPI {
    private $apiKey;
    private $baseUrl = 'https://image.nalitek.com';

    public function __construct($adminApiKey) {
        $this->apiKey = $adminApiKey;
    }

    public function getUsers($page = 1, $perPage = 50) {
        $url = $this->baseUrl . '/api/admin/users';
        $url .= '?' . http_build_query(['page' => $page, 'per_page' => $perPage]);

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $this->apiKey]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 200) {
            $data = json_decode($response, true);
            echo "👥 Total users: {$data['total']}\n";

            foreach ($data['users'] as $user) {
                echo "\n👤 {$user['username']} ({$user['email']})\n";
                echo "   Plan: {$user['plan']}\n";
                echo "   Quota: {$user['quota']['daily_generations']}/day\n";
                echo "   Created: {$user['created_at']}\n";
            }

            return $data;
        } else {
            echo "❌ Error: HTTP $httpCode\n";
            return null;
        }
    }

    public function getStatistics() {
        $url = $this->baseUrl . '/api/admin/statistics';

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $this->apiKey]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 200) {
            $stats = json_decode($response, true);
            echo "📊 System Statistics:\n";
            echo "   Total users: {$stats['total_users']}\n";
            echo "   Active users (24h): {$stats['active_users_24h']}\n";
            echo "   Total generations: {$stats['total_generations']}\n";
            echo "   Generations today: {$stats['generations_today']}\n";
            echo "   Storage used: {$stats['storage_used_gb']} GB\n";
            echo "   Revenue this month: \${$stats['revenue_month']}\n";
            return $stats;
        } else {
            echo "❌ Error: HTTP $httpCode\n";
            return null;
        }
    }

    public function updateUserQuota($userId, $dailyGenerations, $dailyUpscales) {
        $url = $this->baseUrl . "/api/admin/user/$userId/quota";

        $data = [
            'daily_generations' => $dailyGenerations,
            'daily_upscales' => $dailyUpscales
        ];

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'X-API-Key: ' . $this->apiKey,
            'Content-Type: application/json'
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 200) {
            echo "✅ Quota updated for user $userId\n";
            return true;
        } else {
            echo "❌ Error: HTTP $httpCode\n";
            return false;
        }
    }
}

// Использование
$admin = new AdminAPI('admin-api-key');

// Получить пользователей
$admin->getUsers(1);

// Получить статистику
$admin->getStatistics();

// Обновить квоты
$admin->updateUserQuota(123, 500, 100);

LoRA Модели

GET /api/loras

Получение списка доступных LoRA моделей для стилизации.

Примеры использования

# Получить список всех доступных LoRA моделей
curl -X GET https://image.nalitek.com/api/loras \
  -H "X-API-Key: your-api-key"

# Использование LoRA при генерации
curl -X POST https://image.nalitek.com/api/generate \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "anime character with detailed eyes, manga style",
    "lora_id": "anime-style-v2",
    "width": 1024,
    "height": 1024
  }'

# Использование нескольких LoRA в img2img
curl -X POST https://image.nalitek.com/api/img2img \
  -H "X-API-Key: your-api-key" \
  -F "image=@photo.jpg" \
  -F "prompt=detailed portrait" \
  -F "lora_models[]=anime-style-v2" \
  -F "lora_models[]=detailed-eyes-v1" \
  -F "strength=0.7"

Успешный ответ

{
    "loras": [
        {
            "id": "anime-style-v2",
            "name": "Anime Style v2",
            "description": "Стиль аниме для персонажей",
            "trigger_words": ["anime", "manga"],
            "example_prompt": "anime character, detailed eyes",
            "thumbnail_url": "https://image.nalitek.com/uploads/loras/previews/anime-v2_preview.jpg",
            "is_active": true,
            "is_public": true,
            "created_at": "2025-01-10T08:00:00Z",
            "category": "style",
            "weight_recommended": 0.8
        }
    ]
}

Категории LoRA моделей

  • style - Художественные стили (аниме, комикс, живопись)
  • character - Персонажи и типажи
  • concept - Концепции и тематики
  • architecture - Архитектурные стили
  • nature - Природа и пейзажи

Статистика пользователя

GET /api/user/stats

Получение статистики использования API и информации о квотах.

Пример запроса

# Получить статистику использования
curl -X GET https://image.nalitek.com/api/user/stats \
  -H "X-API-Key: your-api-key"

# Форматированный вывод с jq
curl -X GET https://image.nalitek.com/api/user/stats \
  -H "X-API-Key: your-api-key" \
  | jq '.'

# Проверить оставшиеся квоты
curl -X GET https://image.nalitek.com/api/user/stats \
  -H "X-API-Key: your-api-key" \
  | jq '.quota'

Успешный ответ

{
    "user": {
        "id": 1,
        "username": "john_doe",
        "email": "john@example.com",
        "role": "user",
        "plan": "pro"
    },
    "quota": {
        "daily_generations": 100,
        "daily_upscales": 50,
        "used_generations": 37,
        "used_upscales": 8,
        "remaining_generations": 63,
        "remaining_upscales": 42,
        "reset_at": "2025-01-16T00:00:00Z"
    },
    "storage": {
        "used_mb": 3276.8,
        "limit_mb": 10240,
        "percent_used": 32.0
    },
    "statistics": {
        "total_generations": 1847,
        "total_upscales": 423,
        "total_img2img": 156,
        "average_generation_time": 31.2,
        "favorite_count": 89,
        "created_at": "2024-12-01T10:00:00Z"
    }
}

WebSocket События

Для получения обновлений в реальном времени используйте Socket.IO подключение.

Подключение

const socket = io('https://image.nalitek.com', {
    auth: {
        token: 'your-api-key'
    }
});

// Подписка на канал генерации
socket.emit('subscribe', { channel: 'generation_4738' });

// Обработка событий
socket.on('generation_progress', (data) => {
    console.log(`Progress: ${data.progress}%`);
});

События генерации

generation_started

{
    "image_id": 4738,
    "message": "Генерация началась"
}

generation_progress

{
    "image_id": 4738,
    "progress": 47,
    "current_step": 14,
    "total_steps": 30,
    "phase": "generating",
    "message": "Генерация изображения (Шаг 14/30)..."
}

generation_complete

{
    "image_id": 4738,
    "url": "https://image.nalitek.com/uploads/images/flux_20250115_145230_b3cd4567.png",
    "thumbnail_url": "https://image.nalitek.com/uploads/images/thumbnails/flux_20250115_145230_b3cd4567_thumb.png",
    "seed": 8472659,
    "time": 27.3,
    "status": "completed"
}

Batch API - Пакетная обработка

Batch API позволяет отправлять несколько запросов генерации одновременно для более эффективной обработки.

POST /api/batch/generate Rate limit: 5/min

Пакетная генерация изображений

Отправка до 10 запросов генерации в одном вызове API.

Параметры запроса

Параметр Тип Обязательный Описание
batch array Да Массив запросов генерации (макс. 10)
batch[].prompt string Да Текстовое описание для каждого изображения
batch[].params object Нет Параметры генерации (width, height, steps и т.д.)
batch[].id string Нет Клиентский ID для отслеживания
parallel boolean Нет Параллельная обработка (default: false)
priority string Нет Приоритет: low, normal, high (default: normal)

Примеры запросов

cURL
# Пакетная генерация нескольких изображений
curl -X POST https://image.nalitek.com/api/batch/generate \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "batch": [
        {
            "id": "req_001",
            "prompt": "futuristic city at night",
            "params": {
                "width": 1024,
                "height": 1024,
                "steps": 30,
                "seed": 42
            }
        },
        {
            "id": "req_002",
            "prompt": "magical forest with fireflies",
            "params": {
                "width": 1920,
                "height": 1080,
                "smart_upscale": true,
                "target_width": 3840,
                "target_height": 2160
            }
        }
    ],
    "parallel": true,
    "priority": "high"
  }'

# Проверка статуса пакета
curl -X GET https://image.nalitek.com/api/batch/batch_1736856789_a5f3b2c1 \
  -H "X-API-Key: your-api-key"

# Отмена пакетной обработки
curl -X DELETE https://image.nalitek.com/api/batch/batch_1736856789_a5f3b2c1 \
  -H "X-API-Key: your-api-key"
JSON структура
{
    "batch": [
        {
            "id": "req_001",
            "prompt": "futuristic city at night",
            "params": {
                "width": 1024,
                "height": 1024,
                "steps": 30,
                "seed": 42
            }
        },
        {
            "id": "req_002",
            "prompt": "magical forest with fireflies",
            "params": {
                "width": 1920,
                "height": 1080,
                "smart_upscale": true,
                "target_width": 3840,
                "target_height": 2160
            }
        },
        {
            "id": "req_003",
            "prompt": "underwater ancient ruins",
            "params": {
                "model": "flux-schnell",
                "steps": 20
            }
        }
    ],
    "parallel": true,
    "priority": "high"
}

Успешный ответ (202 Accepted)

{
    "batch_id": "batch_1736856789_a5f3b2c1",
    "status": "processing",
    "total": 3,
    "queued": 3,
    "processing": 0,
    "completed": 0,
    "failed": 0,
    "jobs": [
        {
            "id": "req_001",
            "image_id": 4739,
            "status": "queued",
            "position": 1
        },
        {
            "id": "req_002", 
            "image_id": 4740,
            "status": "queued",
            "position": 2
        },
        {
            "id": "req_003",
            "image_id": 4741,
            "status": "queued",
            "position": 3
        }
    ],
    "status_url": "/api/batch/batch_1736856789_a5f3b2c1",
    "websocket_channel": "batch_1736856789_a5f3b2c1"
}
GET /api/batch/{batch_id} No limit

Статус пакетной обработки

Получение статуса всех задач в пакете.

Успешный ответ

{
    "batch_id": "batch_1736856789_a5f3b2c1",
    "status": "completed",
    "total": 3,
    "queued": 0,
    "processing": 0,
    "completed": 3,
    "failed": 0,
    "started_at": "2025-01-15T10:00:00Z",
    "completed_at": "2025-01-15T10:02:30Z",
    "total_time": 150.5,
    "jobs": [
        {
            "id": "req_001",
            "image_id": 4739,
            "status": "completed",
            "url": "https://image.nalitek.com/uploads/images/flux_20250115_150100_xyz98765.png",
            "seed": 2847391,
            "generation_time": 45.2
        },
        {
            "id": "req_002",
            "image_id": 4740,
            "status": "completed",
            "url": "https://image.nalitek.com/uploads/images/flux_20250115_145245_def45678.png",
            "seed": 7263849,
            "generation_time": 62.1
        },
        {
            "id": "req_003",
            "image_id": 4741,
            "status": "completed",
            "url": "https://image.nalitek.com/uploads/images/flux_20250115_145300_ghi78901.png",
            "seed": 9182736,
            "generation_time": 43.2
        }
    ]
}
POST /api/batch/img2img Rate limit: 5/min

Пакетная трансформация изображений

Применение различных трансформаций к одному или нескольким изображениям.

Параметры запроса (multipart/form-data)

Параметр Тип Описание
images[] file[] Массив изображений для обработки
prompts[] string[] Массив промптов для каждого изображения
shared_params json Общие параметры для всех изображений

Примеры использования

cURL
# Пакетная трансформация нескольких изображений
curl -X POST https://image.nalitek.com/api/batch/img2img \
  -H "X-API-Key: your-api-key" \
  -F "images[]=@image1.jpg" \
  -F "images[]=@image2.jpg" \
  -F "images[]=@image3.jpg" \
  -F "prompts[]=convert to cyberpunk style" \
  -F "prompts[]=make it look vintage" \
  -F "prompts[]=transform to anime style" \
  -F 'shared_params={"strength": 0.75, "steps": 30, "smart_upscale": true, "target_width": 2048, "target_height": 2048}'

# Одно изображение с разными вариациями
curl -X POST https://image.nalitek.com/api/batch/img2img \
  -H "X-API-Key: your-api-key" \
  -F "images[]=@portrait.jpg" \
  -F "prompts[]=oil painting style" \
  -F "prompts[]=watercolor style" \
  -F "prompts[]=pencil sketch style" \
  -F 'shared_params={"strength": 0.8, "steps": 40}'

# С LoRA моделями
curl -X POST https://image.nalitek.com/api/batch/img2img \
  -H "X-API-Key: your-api-key" \
  -F "images[]=@photo1.jpg" \
  -F "images[]=@photo2.jpg" \
  -F "prompts[]=anime character with detailed eyes" \
  -F "prompts[]=manga style illustration" \
  -F 'shared_params={"lora_models": ["anime-style-v2", "detailed-eyes-v1"], "strength": 0.7}'
JavaScript
const formData = new FormData();

// Добавляем изображения
formData.append('images[]', file1);
formData.append('images[]', file2);

// Добавляем промпты
formData.append('prompts[]', 'convert to cyberpunk style');
formData.append('prompts[]', 'make it look vintage');

// Общие параметры
formData.append('shared_params', JSON.stringify({
    strength: 0.75,
    steps: 30,
    smart_upscale: true,
    target_width: 2048,
    target_height: 2048
}));

const response = await fetch('/api/batch/img2img', {
    method: 'POST',
    headers: {
        'X-API-Key': 'your-api-key'
    },
    body: formData
});
DELETE /api/batch/{batch_id} Rate limit: 30/min

Отмена пакетной обработки

Отмена всех незавершенных задач в пакете.

Успешный ответ

{
    "batch_id": "batch_1736856789_a5f3b2c1",
    "cancelled": 2,
    "completed": 1,
    "message": "Batch processing cancelled"
}

Особенности Batch API

  • Эффективность: Экономия на накладных расходах HTTP
  • Приоритизация: Управление очередью обработки
  • Атомарность: Каждая задача обрабатывается независимо
  • Отслеживание: WebSocket уведомления для каждой задачи
  • Квоты: Учитывается как отдельные генерации

WebSocket события для Batch

// Подписка на batch канал
socket.emit('subscribe', { channel: 'batch_1736856789_a5f3b2c1' });

// События
socket.on('batch_started', (data) => {
    console.log('Batch processing started');
});

socket.on('job_completed', (data) => {
    console.log(`Job ${data.job_id} completed`);
});

socket.on('batch_completed', (data) => {
    console.log('All jobs completed');
});

🎯 Лучшие практики

  • Используйте batch для генерации вариаций одного концепта
  • Группируйте запросы с похожими параметрами
  • Устанавливайте priority для срочных пакетов
  • Используйте client ID для удобного отслеживания
  • Обрабатывайте частичные ошибки в пакете

Webhook Интеграция

Для получения уведомлений о завершении генерации можно настроить webhook в параметрах запроса:

Настройка webhook

{
    "prompt": "beautiful landscape",
    "webhook_url": "https://your-server.com/webhook",
    "webhook_events": ["generation.complete", "generation.failed"]
}

Формат webhook запроса

Webhook получит POST запрос с следующей структурой:

{
    "event": "generation.complete",
    "timestamp": "2025-01-15T10:30:00Z",
    "data": {
        "image_id": 123,
        "url": "https://image.nalitek.com/uploads/images/flux_20250115_150100_xyz98765.png",
        "prompt": "futuristic city at night, neon lights, cyberpunk style, ultra detailed",
        "seed": 8472659,
        "generation_time": 25.5
    },
    "signature": "sha256=..."  // HMAC подпись для верификации
}

Доступные события

  • generation.started - генерация началась
  • generation.progress - прогресс генерации
  • generation.complete - генерация завершена успешно
  • generation.failed - ошибка генерации
  • upscale.complete - upscale завершен
  • upscale.failed - ошибка upscale

Лимиты и квоты

⚡ Rate Limiting по эндпоинтам

Эндпоинт Лимит Окно времени
/api/generate 10 запросов 1 минута
/api/img2img 10 запросов 1 минута
/api/upscale/{id} 5 запросов 1 минута
/api/images 60 запросов 1 минута
/api/batch/generate 2 запроса 1 минута

📊 Дневные квоты по планам

План Генераций/день Img2Img/день Upscale/день Макс. разрешение Хранилище
Free 10 5 5 1024×1024 100 MB
Pro 100 50 50 2048×2048 1 GB
Business 1000 500 500 2048×2048 10 GB
Enterprise Без лимитов Без лимитов Без лимитов 4096×4096 Без лимитов

Коды ошибок

Код Статус Описание Решение
200 OK Успешный запрос -
201 Created Ресурс создан -
400 Bad Request Неверные параметры запроса Проверьте формат данных и обязательные поля
401 Unauthorized Отсутствует или неверный API ключ Добавьте заголовок X-API-Key
403 Forbidden Превышен лимит генераций Дождитесь сброса квоты в 00:00 UTC
404 Not Found Ресурс не найден Проверьте ID или URL
429 Too Many Requests Превышен rate limit Смотрите заголовок Retry-After
500 Internal Server Error Ошибка сервера Повторите позже

Формат ошибок

{
    "error": "Краткое описание ошибки",
    "details": "Детальное описание проблемы",
    "code": "ERROR_CODE",
    "request_id": "req_abc123def456"
}

Модели и форматы

🤖 Доступные модели

  • flux-dev - основная модель FLUX для высокого качества (30-50 шагов)
  • flux-schnell - быстрая модель FLUX (10-20 шагов)

🖼️ Поддерживаемые форматы изображений

Входные форматы PNG, JPEG, WebP
Выходные форматы PNG, JPEG, WebP
Максимальный размер файла 10 MB

📐 Ограничения размеров

Минимум 512×512
Максимум (Free) 1024×1024
Максимум (Pro/Business) 2048×2048
Максимум (Enterprise) 4096×4096
Шаг изменения 64 пикселя
Рекомендуемые размеры 768×768, 1024×1024, 1920×1080

Полные примеры интеграции

Готовые примеры для быстрой интеграции API в ваши проекты.

# flux_api_sdk.py
"""
FLUX API SDK for Python
Complete integration example
"""

import requests
import time
import json
from typing import Optional, Dict, Any, List
from enum import Enum

class Model(Enum):
    FLUX_DEV = "flux-dev"
    FLUX_SCHNELL = "flux-schnell"

class ImageFormat(Enum):
    PNG = "png"
    JPEG = "jpeg"
    WEBP = "webp"

class FluxAPISDK:
    """Complete FLUX API SDK for Python"""

    def __init__(self, api_key: str, base_url: str = "https://image.nalitek.com"):
        self.api_key = api_key
        self.base_url = base_url
        self.headers = {
            "X-API-Key": api_key,
            "Content-Type": "application/json"
        }

    def generate(
        self,
        prompt: str,
        negative_prompt: str = "",
        width: int = 1024,
        height: int = 1024,
        steps: int = 30,
        guidance_scale: float = 7.5,
        seed: int = -1,
        model: Model = Model.FLUX_DEV,
        format: ImageFormat = ImageFormat.PNG,
        lora_id: Optional[str] = None,
        webhook_url: Optional[str] = None,
        wait_for_completion: bool = True
    ) -> Dict[str, Any]:
        """
        Generate an image from text prompt

        Args:
            prompt: Text description of desired image
            negative_prompt: What to exclude from generation
            width: Image width (512-2048, divisible by 64)
            height: Image height (512-2048, divisible by 64)
            steps: Number of generation steps (10-100)
            guidance_scale: Prompt adherence strength (1.0-20.0)
            seed: Seed for reproducibility (-1 for random)
            model: Model to use (FLUX_DEV or FLUX_SCHNELL)
            format: Output format (PNG, JPEG, or WEBP)
            lora_id: LoRA model ID for styling
            webhook_url: URL for completion notifications
            wait_for_completion: Whether to wait for generation to complete

        Returns:
            Dictionary with image data or generation status
        """

        url = f"{self.base_url}/api/generate"

        data = {
            "prompt": prompt,
            "negative_prompt": negative_prompt,
            "width": width,
            "height": height,
            "steps": steps,
            "guidance_scale": guidance_scale,
            "seed": seed,
            "model": model.value,
            "format": format.value
        }

        if lora_id:
            data["lora_id"] = lora_id

        if webhook_url:
            data["webhook_url"] = webhook_url

        response = requests.post(url, json=data, headers=self.headers)

        if response.status_code == 201:
            result = response.json()

            if wait_for_completion:
                return self._wait_for_image(result["image_id"])

            return result
        else:
            raise Exception(f"API Error {response.status_code}: {response.text}")

    def img2img(
        self,
        image_path: str,
        prompt: str,
        negative_prompt: str = "",
        strength: float = 0.75,
        steps: int = 30,
        guidance_scale: float = 7.5,
        seed: int = -1,
        wait_for_completion: bool = True
    ) -> Dict[str, Any]:
        """
        Transform an existing image based on text prompt

        Args:
            image_path: Path to input image file
            prompt: Description of desired transformation
            negative_prompt: What to exclude from generation
            strength: Transformation strength (0.0-1.0)
            steps: Number of generation steps (10-100)
            guidance_scale: Prompt adherence strength (1.0-20.0)
            seed: Seed for reproducibility (-1 for random)
            wait_for_completion: Whether to wait for completion

        Returns:
            Dictionary with transformed image data
        """

        url = f"{self.base_url}/api/img2img"

        with open(image_path, 'rb') as f:
            files = {'image': (image_path, f, 'image/png')}

            data = {
                'prompt': prompt,
                'negative_prompt': negative_prompt,
                'strength': strength,
                'steps': steps,
                'guidance_scale': guidance_scale,
                'seed': seed
            }

            headers = {"X-API-Key": self.api_key}
            response = requests.post(url, headers=headers, files=files, data=data)

        if response.status_code == 201:
            result = response.json()

            if wait_for_completion and 'image_id' in result:
                return self._wait_for_image(result["image_id"])

            return result
        else:
            raise Exception(f"API Error {response.status_code}: {response.text}")

    def upscale(
        self,
        image_id: int,
        scale: int = 2,
        face_enhance: bool = False,
        denoise: float = 0.5
    ) -> Dict[str, Any]:
        """
        Upscale an existing image

        Args:
            image_id: ID of image to upscale
            scale: Upscaling factor (2 or 4)
            face_enhance: Whether to enhance faces with GFPGAN
            denoise: Denoising level (0.0-1.0)

        Returns:
            Dictionary with upscaled image data
        """

        url = f"{self.base_url}/api/upscale/{image_id}"

        data = {
            "scale": scale,
            "face_enhance": face_enhance,
            "denoise": denoise
        }

        response = requests.post(url, json=data, headers=self.headers)

        if response.status_code == 201:
            return response.json()
        else:
            raise Exception(f"API Error {response.status_code}: {response.text}")

    def get_image(self, image_id: int) -> Dict[str, Any]:
        """Get details of a specific image"""

        url = f"{self.base_url}/api/images/{image_id}"
        response = requests.get(url, headers=self.headers)

        if response.status_code == 200:
            return response.json()
        else:
            raise Exception(f"API Error {response.status_code}: {response.text}")

    def list_images(
        self,
        page: int = 1,
        per_page: int = 20,
        status: Optional[str] = None,
        is_upscaled: Optional[bool] = None,
        sort: str = "date_desc"
    ) -> Dict[str, Any]:
        """
        List user's images with pagination and filters

        Args:
            page: Page number
            per_page: Items per page (1-100)
            status: Filter by status (generating/completed/failed)
            is_upscaled: Filter by upscale status
            sort: Sort order (date_desc/date_asc)

        Returns:
            Dictionary with paginated image list
        """

        url = f"{self.base_url}/api/images"

        params = {
            'page': page,
            'per_page': per_page,
            'sort': sort
        }

        if status:
            params['status'] = status
        if is_upscaled is not None:
            params['is_upscaled'] = is_upscaled

        response = requests.get(url, params=params, headers=self.headers)

        if response.status_code == 200:
            return response.json()
        else:
            raise Exception(f"API Error {response.status_code}: {response.text}")

    def delete_image(self, image_id: int) -> bool:
        """Delete an image"""

        url = f"{self.base_url}/api/images/{image_id}"
        response = requests.delete(url, headers=self.headers)

        return response.status_code == 204

    def get_loras(self) -> List[Dict[str, Any]]:
        """Get list of available LoRA models"""

        url = f"{self.base_url}/api/loras"
        response = requests.get(url, headers=self.headers)

        if response.status_code == 200:
            return response.json()['loras']
        else:
            raise Exception(f"API Error {response.status_code}: {response.text}")

    def get_user_stats(self) -> Dict[str, Any]:
        """Get user statistics and quota information"""

        url = f"{self.base_url}/api/user/stats"
        response = requests.get(url, headers=self.headers)

        if response.status_code == 200:
            return response.json()
        else:
            raise Exception(f"API Error {response.status_code}: {response.text}")

    def check_api_status(self) -> Dict[str, Any]:
        """Check API status and service availability"""

        url = f"{self.base_url}/api/status"
        response = requests.get(url)

        if response.status_code == 200:
            return response.json()
        else:
            raise Exception(f"API Error {response.status_code}: {response.text}")

    def batch_generate(
        self,
        prompts: List[str],
        common_params: Optional[Dict[str, Any]] = None,
        webhook_url: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Generate multiple images in batch

        Args:
            prompts: List of prompts (max 10)
            common_params: Common parameters for all generations
            webhook_url: URL for batch completion notification

        Returns:
            Dictionary with batch information
        """

        url = f"{self.base_url}/api/batch/generate"

        batch = []
        for prompt in prompts[:10]:  # Limit to 10
            item = {"prompt": prompt}
            if common_params:
                item.update(common_params)
            batch.append(item)

        data = {"batch": batch}
        if webhook_url:
            data["webhook_url"] = webhook_url

        response = requests.post(url, json=data, headers=self.headers)

        if response.status_code == 201:
            return response.json()
        else:
            raise Exception(f"API Error {response.status_code}: {response.text}")

    def _wait_for_image(self, image_id: int, timeout: int = 120) -> Dict[str, Any]:
        """Wait for image generation to complete"""

        start_time = time.time()

        while time.time() - start_time < timeout:
            image = self.get_image(image_id)

            if image['status'] == 'completed':
                return image
            elif image['status'] == 'failed':
                raise Exception(f"Generation failed: {image.get('error_message')}")

            time.sleep(2)

        raise Exception(f"Generation timeout after {timeout} seconds")

# Example usage
if __name__ == "__main__":
    # Initialize SDK
    sdk = FluxAPISDK("your-api-key")

    # Check API status
    status = sdk.check_api_status()
    print(f"API Status: {status['status']}")

    # Generate an image
    try:
        image = sdk.generate(
            prompt="beautiful sunset over mountains, professional photography",
            width=1024,
            height=1024,
            steps=30,
            model=Model.FLUX_DEV,
            format=ImageFormat.PNG
        )
        print(f"Generated image: {image['url']}")

        # Upscale the image
        upscaled = sdk.upscale(image['id'], scale=2)
        print(f"Upscaled image: {upscaled['url']}")

    except Exception as e:
        print(f"Error: {e}")

    # Get user statistics
    stats = sdk.get_user_stats()
    print(f"Remaining generations: {stats['quota']['remaining_generations']}")

    # List available LoRA models
    loras = sdk.get_loras()
    for lora in loras:
        print(f"LoRA: {lora['name']} ({lora['id']})")
// flux-api-sdk.js
/**
 * FLUX API SDK for JavaScript
 * Complete integration example
 */

class FluxAPISDK {
    constructor(apiKey, baseUrl = 'https://image.nalitek.com') {
        this.apiKey = apiKey;
        this.baseUrl = baseUrl;
    }

    /**
     * Generate an image from text prompt
     */
    async generate(options = {}) {
        const url = `${this.baseUrl}/api/generate`;

        const data = {
            prompt: options.prompt,
            negative_prompt: options.negative_prompt || '',
            width: options.width || 1024,
            height: options.height || 1024,
            steps: options.steps || 30,
            guidance_scale: options.guidance_scale || 7.5,
            seed: options.seed || -1,
            model: options.model || 'flux-dev',
            format: options.format || 'png'
        };

        if (options.lora_id) data.lora_id = options.lora_id;
        if (options.webhook_url) data.webhook_url = options.webhook_url;

        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'X-API-Key': this.apiKey,
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(data)
        });

        if (response.status === 201) {
            const result = await response.json();

            if (options.waitForCompletion !== false) {
                return await this.waitForImage(result.image_id);
            }

            return result;
        } else {
            const error = await response.json();
            throw new Error(`API Error ${response.status}: ${error.error}`);
        }
    }

    /**
     * Transform an existing image
     */
    async img2img(imageFile, options = {}) {
        const url = `${this.baseUrl}/api/img2img`;

        const formData = new FormData();
        formData.append('image', imageFile);
        formData.append('prompt', options.prompt);
        formData.append('negative_prompt', options.negative_prompt || '');
        formData.append('strength', options.strength || 0.75);
        formData.append('steps', options.steps || 30);
        formData.append('guidance_scale', options.guidance_scale || 7.5);
        formData.append('seed', options.seed || -1);

        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'X-API-Key': this.apiKey
            },
            body: formData
        });

        if (response.status === 201) {
            const result = await response.json();

            if (options.waitForCompletion !== false && result.image_id) {
                return await this.waitForImage(result.image_id);
            }

            return result;
        } else {
            const error = await response.json();
            throw new Error(`API Error ${response.status}: ${error.error}`);
        }
    }

    /**
     * Upscale an image
     */
    async upscale(imageId, options = {}) {
        const url = `${this.baseUrl}/api/upscale/${imageId}`;

        const data = {
            scale: options.scale || 2,
            face_enhance: options.face_enhance || false,
            denoise: options.denoise || 0.5
        };

        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'X-API-Key': this.apiKey,
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(data)
        });

        if (response.status === 201) {
            return await response.json();
        } else {
            const error = await response.json();
            throw new Error(`API Error ${response.status}: ${error.error}`);
        }
    }

    /**
     * Get image details
     */
    async getImage(imageId) {
        const url = `${this.baseUrl}/api/images/${imageId}`;

        const response = await fetch(url, {
            headers: { 'X-API-Key': this.apiKey }
        });

        if (response.status === 200) {
            return await response.json();
        } else {
            const error = await response.json();
            throw new Error(`API Error ${response.status}: ${error.error}`);
        }
    }

    /**
     * List images with filters
     */
    async listImages(options = {}) {
        const url = new URL(`${this.baseUrl}/api/images`);

        url.searchParams.append('page', options.page || 1);
        url.searchParams.append('per_page', options.per_page || 20);
        url.searchParams.append('sort', options.sort || 'date_desc');

        if (options.status) url.searchParams.append('status', options.status);
        if (options.is_upscaled !== undefined) {
            url.searchParams.append('is_upscaled', options.is_upscaled);
        }

        const response = await fetch(url, {
            headers: { 'X-API-Key': this.apiKey }
        });

        if (response.status === 200) {
            return await response.json();
        } else {
            const error = await response.json();
            throw new Error(`API Error ${response.status}: ${error.error}`);
        }
    }

    /**
     * Delete an image
     */
    async deleteImage(imageId) {
        const url = `${this.baseUrl}/api/images/${imageId}`;

        const response = await fetch(url, {
            method: 'DELETE',
            headers: { 'X-API-Key': this.apiKey }
        });

        return response.status === 204;
    }

    /**
     * Get available LoRA models
     */
    async getLoras() {
        const url = `${this.baseUrl}/api/loras`;

        const response = await fetch(url, {
            headers: { 'X-API-Key': this.apiKey }
        });

        if (response.status === 200) {
            const data = await response.json();
            return data.loras;
        } else {
            const error = await response.json();
            throw new Error(`API Error ${response.status}: ${error.error}`);
        }
    }

    /**
     * Get user statistics
     */
    async getUserStats() {
        const url = `${this.baseUrl}/api/user/stats`;

        const response = await fetch(url, {
            headers: { 'X-API-Key': this.apiKey }
        });

        if (response.status === 200) {
            return await response.json();
        } else {
            const error = await response.json();
            throw new Error(`API Error ${response.status}: ${error.error}`);
        }
    }

    /**
     * Check API status
     */
    async checkApiStatus() {
        const url = `${this.baseUrl}/api/status`;

        const response = await fetch(url);

        if (response.status === 200) {
            return await response.json();
        } else {
            throw new Error(`API Error ${response.status}`);
        }
    }

    /**
     * Batch generate images
     */
    async batchGenerate(prompts, commonParams = {}, webhookUrl = null) {
        const url = `${this.baseUrl}/api/batch/generate`;

        const batch = prompts.slice(0, 10).map(prompt => ({
            prompt,
            ...commonParams
        }));

        const data = { batch };
        if (webhookUrl) data.webhook_url = webhookUrl;

        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'X-API-Key': this.apiKey,
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(data)
        });

        if (response.status === 201) {
            return await response.json();
        } else {
            const error = await response.json();
            throw new Error(`API Error ${response.status}: ${error.error}`);
        }
    }

    /**
     * Wait for image generation to complete
     */
    async waitForImage(imageId, timeout = 120000) {
        const startTime = Date.now();

        while (Date.now() - startTime < timeout) {
            const image = await this.getImage(imageId);

            if (image.status === 'completed') {
                return image;
            } else if (image.status === 'failed') {
                throw new Error(`Generation failed: ${image.error_message}`);
            }

            await new Promise(resolve => setTimeout(resolve, 2000));
        }

        throw new Error(`Generation timeout after ${timeout}ms`);
    }
}

// Example usage
(async () => {
    // Initialize SDK
    const sdk = new FluxAPISDK('your-api-key');

    try {
        // Check API status
        const status = await sdk.checkApiStatus();
        console.log(`API Status: ${status.status}`);

        // Generate an image
        const image = await sdk.generate({
            prompt: 'beautiful sunset over mountains, professional photography',
            width: 1024,
            height: 1024,
            steps: 30,
            model: 'flux-dev'
        });
        console.log(`Generated image: ${image.url}`);

        // Upscale the image
        const upscaled = await sdk.upscale(image.id, { scale: 2 });
        console.log(`Upscaled image: ${upscaled.url}`);

        // Get user statistics
        const stats = await sdk.getUserStats();
        console.log(`Remaining generations: ${stats.quota.remaining_generations}`);

        // List available LoRA models
        const loras = await sdk.getLoras();
        loras.forEach(lora => {
            console.log(`LoRA: ${lora.name} (${lora.id})`);
        });

    } catch (error) {
        console.error(`Error: ${error.message}`);
    }
})();

// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
    module.exports = FluxAPISDK;
}
<?php
/**
 * FLUX API SDK for PHP
 * Complete integration example
 */

class FluxAPISDK {
    private $apiKey;
    private $baseUrl;

    public function __construct($apiKey, $baseUrl = 'https://image.nalitek.com') {
        $this->apiKey = $apiKey;
        $this->baseUrl = $baseUrl;
    }

    /**
     * Generate an image from text prompt
     */
    public function generate($options = []) {
        $url = $this->baseUrl . '/api/generate';

        $data = array_merge([
            'prompt' => '',
            'negative_prompt' => '',
            'width' => 1024,
            'height' => 1024,
            'steps' => 30,
            'guidance_scale' => 7.5,
            'seed' => -1,
            'model' => 'flux-dev',
            'format' => 'png'
        ], $options);

        $response = $this->makeRequest('POST', $url, $data);

        if ($response['status'] === 201) {
            $result = $response['data'];

            if (!isset($options['wait_for_completion']) || $options['wait_for_completion']) {
                return $this->waitForImage($result['image_id']);
            }

            return $result;
        } else {
            throw new Exception("API Error {$response['status']}: {$response['data']['error']}");
        }
    }

    /**
     * Transform an existing image
     */
    public function img2img($imagePath, $options = []) {
        $url = $this->baseUrl . '/api/img2img';

        $cFile = new CURLFile($imagePath, mime_content_type($imagePath), basename($imagePath));

        $data = array_merge([
            'image' => $cFile,
            'prompt' => '',
            'negative_prompt' => '',
            'strength' => 0.75,
            'steps' => 30,
            'guidance_scale' => 7.5,
            'seed' => -1
        ], $options);

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-API-Key: ' . $this->apiKey]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        $responseData = json_decode($response, true);

        if ($httpCode === 201) {
            if (!isset($options['wait_for_completion']) || $options['wait_for_completion']) {
                if (isset($responseData['image_id'])) {
                    return $this->waitForImage($responseData['image_id']);
                }
            }
            return $responseData;
        } else {
            throw new Exception("API Error $httpCode: {$responseData['error']}");
        }
    }

    /**
     * Upscale an image
     */
    public function upscale($imageId, $options = []) {
        $url = $this->baseUrl . "/api/upscale/$imageId";

        $data = array_merge([
            'scale' => 2,
            'face_enhance' => false,
            'denoise' => 0.5
        ], $options);

        $response = $this->makeRequest('POST', $url, $data);

        if ($response['status'] === 201) {
            return $response['data'];
        } else {
            throw new Exception("API Error {$response['status']}: {$response['data']['error']}");
        }
    }

    /**
     * Get image details
     */
    public function getImage($imageId) {
        $url = $this->baseUrl . "/api/images/$imageId";

        $response = $this->makeRequest('GET', $url);

        if ($response['status'] === 200) {
            return $response['data'];
        } else {
            throw new Exception("API Error {$response['status']}: {$response['data']['error']}");
        }
    }

    /**
     * List images with filters
     */
    public function listImages($options = []) {
        $url = $this->baseUrl . '/api/images';

        $params = array_merge([
            'page' => 1,
            'per_page' => 20,
            'sort' => 'date_desc'
        ], $options);

        $url .= '?' . http_build_query($params);

        $response = $this->makeRequest('GET', $url);

        if ($response['status'] === 200) {
            return $response['data'];
        } else {
            throw new Exception("API Error {$response['status']}: {$response['data']['error']}");
        }
    }

    /**
     * Delete an image
     */
    public function deleteImage($imageId) {
        $url = $this->baseUrl . "/api/images/$imageId";

        $response = $this->makeRequest('DELETE', $url);

        return $response['status'] === 204;
    }

    /**
     * Get available LoRA models
     */
    public function getLoras() {
        $url = $this->baseUrl . '/api/loras';

        $response = $this->makeRequest('GET', $url);

        if ($response['status'] === 200) {
            return $response['data']['loras'];
        } else {
            throw new Exception("API Error {$response['status']}: {$response['data']['error']}");
        }
    }

    /**
     * Get user statistics
     */
    public function getUserStats() {
        $url = $this->baseUrl . '/api/user/stats';

        $response = $this->makeRequest('GET', $url);

        if ($response['status'] === 200) {
            return $response['data'];
        } else {
            throw new Exception("API Error {$response['status']}: {$response['data']['error']}");
        }
    }

    /**
     * Check API status
     */
    public function checkApiStatus() {
        $url = $this->baseUrl . '/api/status';

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 200) {
            return json_decode($response, true);
        } else {
            throw new Exception("API Error $httpCode");
        }
    }

    /**
     * Batch generate images
     */
    public function batchGenerate($prompts, $commonParams = [], $webhookUrl = null) {
        $url = $this->baseUrl . '/api/batch/generate';

        $batch = [];
        foreach (array_slice($prompts, 0, 10) as $prompt) {
            $item = array_merge(['prompt' => $prompt], $commonParams);
            $batch[] = $item;
        }

        $data = ['batch' => $batch];
        if ($webhookUrl) {
            $data['webhook_url'] = $webhookUrl;
        }

        $response = $this->makeRequest('POST', $url, $data);

        if ($response['status'] === 201) {
            return $response['data'];
        } else {
            throw new Exception("API Error {$response['status']}: {$response['data']['error']}");
        }
    }

    /**
     * Wait for image generation to complete
     */
    private function waitForImage($imageId, $timeout = 120) {
        $startTime = time();

        while (time() - $startTime < $timeout) {
            $image = $this->getImage($imageId);

            if ($image['status'] === 'completed') {
                return $image;
            } elseif ($image['status'] === 'failed') {
                throw new Exception("Generation failed: " . $image['error_message']);
            }

            sleep(2);
        }

        throw new Exception("Generation timeout after $timeout seconds");
    }

    /**
     * Make HTTP request
     */
    private function makeRequest($method, $url, $data = null) {
        $ch = curl_init($url);

        if ($method === 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            if ($data) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
            }
        } elseif ($method === 'DELETE') {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
        } elseif ($method === 'PUT') {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
            if ($data) {
                curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
            }
        }

        $headers = ['X-API-Key: ' . $this->apiKey];
        if ($method !== 'GET' && $method !== 'DELETE') {
            $headers[] = 'Content-Type: application/json';
        }

        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        return [
            'status' => $httpCode,
            'data' => json_decode($response, true)
        ];
    }
}

// Example usage
try {
    // Initialize SDK
    $sdk = new FluxAPISDK('your-api-key');

    // Check API status
    $status = $sdk->checkApiStatus();
    echo "API Status: {$status['status']}\n";

    // Generate an image
    $image = $sdk->generate([
        'prompt' => 'beautiful sunset over mountains, professional photography',
        'width' => 1024,
        'height' => 1024,
        'steps' => 30,
        'model' => 'flux-dev'
    ]);
    echo "Generated image: {$image['url']}\n";

    // Upscale the image
    $upscaled = $sdk->upscale($image['id'], ['scale' => 2]);
    echo "Upscaled image: {$upscaled['url']}\n";

    // Get user statistics
    $stats = $sdk->getUserStats();
    echo "Remaining generations: {$stats['quota']['remaining_generations']}\n";

    // List available LoRA models
    $loras = $sdk->getLoras();
    foreach ($loras as $lora) {
        echo "LoRA: {$lora['name']} ({$lora['id']})\n";
    }

} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}