API Documentation
Integrate LeakifyHub's generation capabilities into your applications.
Authentication
All API requests require authentication using your Secret Key. The API uses Bearer token authentication.
Authorization: Bearer sk_live_your_secret_keyBase URL
All API endpoints are accessed through our base URL with /api/v1 prefix.
https://api.leakifyhub.fun/api/v1Pricing
Pay-as-you-go pricing. Credits deducted on submission, refunded if job fails.
Video Generation
$0.20 - $0.30
per request (varies by style)
Photo Generation
$0.10
per request
Faceswap & Custom generation
$0.10
each per request (same rate as catalog photo)
Sandbox Mode
Sandbox mode allows you to test the API without consuming real credits. Sandbox keys return example videos after a simulated 10-second processing delay, making it perfect for development, testing, and integration workflows.
Key Features
- No credits charged - completely free for testing
- Fixed 10-second processing delay to simulate real workflow
- Returns example videos/photos for each style
- Separate statistics tracking for sandbox usage
- Same authentication and response shape as production; includes
POST /faceswap/jobsandPOST /custom-generation/jobs
Creating Sandbox Keys
To create a sandbox key, check the "Sandbox Key" option when creating a new API key in your dashboard. Sandbox keys use the sk_test_ prefix instead of sk_live_.
Authorization: Bearer sk_test_your_sandbox_secret_keyDifferences from Production
| Feature | Production | Sandbox |
|---|---|---|
| Credits Charged | Yes (real cost) | No (free) |
| Processing Time | 1-3 minutes (real) | ~10 seconds (simulated) |
| Results | Generated from your image | Example videos/photos |
| Rate Limit | 100 req/min (job generation), 300-600 req/min (other endpoints) | 300 req/min (job generation), 500-1000 req/min (other endpoints) |
💡 Use Cases
- Testing API integration before going live
- Development and debugging workflows
- Demonstrating API capabilities to clients
- Building and testing webhook handlers
- Validating error handling and edge cases
Available Styles
Styles define the generation type. Video styles generate animated content, photo styles produce static images.
Video Styles (type: "video")
| Style ID | Name | Cost | Options |
|---|---|---|---|
| Loading styles... | |||
Photo Styles (type: "photo")
The Images column shows how many input URLs you send on POST /jobs/generate: Two means you must pass image_url and image_url_2 (same flag as requires_dual_image: true in GET /jobs/styles).
| Style ID | Name | Cost | Images | Options |
|---|---|---|---|---|
| Loading styles... | ||||
Dual-input catalog styles vs faceswap
Only rows with Images: 2 in the table above need a second URL on POST /jobs/generate. In JSON from GET /jobs/styles, those are the photo entries with requires_dual_image: true.
There are no dual-input photo styles in the live catalog right now. Always rely on the Images column or requires_dual_image in GET /jobs/styles — do not guess from the style name alone.
Faceswap is separate: swap a face onto a target body via POST /faceswap/jobs (image_url + face_image_url). Do not use image_url_2 for faceswap.
Style Options Reference
Styles with options require either options.suboption (classic) or options.boolean_selections (boolean-mode). Valid values:
Faceswap
Replace the face on a target image with a face from a second image. Priced at $0.10 per job (same as catalog photo). Poll GET /jobs/{job_id} or use webhook_url like catalog jobs.
Custom generation
Image-to-image generation with a text prompt. The API appends the same face-and-body preservation suffix as the web product. $0.10 per job (same as catalog photo). Use GET /jobs/{job_id} for status and results.
Endpoints
RESTful endpoints for managing generation jobs. Jobs are processed asynchronously.
Usage & Statistics
Monitor your API usage, credits balance, and generation statistics. These endpoints use the same authentication as job endpoints (API keys or Bearer tokens).
API Keys Management
Create and manage your API keys. Important: These endpoints require website JWT authentication (not API key authentication). You must be logged into your account on the website to manage keys.
Authentication: API key management endpoints use website JWT tokens (from your website login), not API keys. Use your website session token with Authorization: Bearer <website_jwt_token>.
File Encryption & Decryption
All generated files are encrypted at rest before being stored on Supabase. Each user's files are encrypted with a unique key derived from their API secret, ensuring privacy and security.
How It Works
- You submit a job with your
API_SECRET - GPU worker derives a unique encryption key from your API secret using PBKDF2
- Generated file is encrypted with this derived key
- Encrypted file is uploaded to storage
- Job response includes
encryption_metadatafor decryption - You derive the same key from your API secret and decrypt the file
Key Derivation
Your encryption key is derived from your API_SECRET using PBKDF2:
- Algorithm: SHA-256
- Salt:
b'leakify_api_secret_salt_v1'(fixed) - Iterations: 100,000
- Key Length: 32 bytes (256 bits)
Important: The same API secret always produces the same encryption key. You only need your API_SECRET to decrypt your files—no master key required!
Encryption Metadata
The job response includes encryption_metadata:
{
"encrypted_key": "base64_encoded_encrypted_file_key",
"iv": "base64_encoded_initialization_vector",
"algorithm": "AES-256-GCM",
"key_version": 1
}encrypted_key: The file's encryption key, encrypted with your derived user key
iv: Initialization vector for file key encryption (fixed: b'file_key_iv_16b')
algorithm: Always "AES-256-GCM"
key_version: Version identifier (currently 1)
Decryption Process
- Download the encrypted file from
result_url - Derive your encryption key from your
API_SECRETusing PBKDF2 - Decrypt the file key from
encryption_metadata.encrypted_keyusing your derived key - Extract the IV (first 12 bytes) from the encrypted file
- Decrypt the file content using the decrypted file key and IV
- Save the decrypted file
⚠️ Security Best Practices
- Never share your API secret—anyone with your secret can decrypt your files
- Store your API secret securely (environment variables, secrets manager)
- Never commit secrets to version control
- If you lose your API secret, you cannot decrypt old files—generate a new key pair for future jobs
Complete Decryption Example
See the Code Examples section above for complete Python and JavaScript implementations that include decryption.
Webhooks
Receive real-time notifications when jobs complete. Include webhook_url in your request.
Webhook Payload
{
"job_id": 12345,
"status": "completed",
"job_type": "video",
"style": "cumshot-facial",
"credits_cost": 0.4,
"result_url": "https://storage.../signed-url",
"result_expires_at": "2026-01-11T12:00:00Z",
"encryption_metadata": {
"encrypted_key": "base64_encoded_encrypted_file_key",
"iv": "base64_encoded_iv",
"algorithm": "AES-256-GCM",
"key_version": 1
}
}Note: The webhook payload includes encryption_metadata for decrypting the file. The result_url is a signed URL that expires in 24 hours.
Code Examples
Complete examples in popular programming languages.
import requests
import base64
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
API_SECRET = "sk_live_your_secret_key"
BASE_URL = "https://api.leakifyhub.fun/api/v1"
def derive_user_key(api_secret: str) -> bytes:
"""Derive encryption key from API secret"""
salt = b'leakify_api_secret_salt_v1'
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
backend=default_backend()
)
return kdf.derive(api_secret.encode('utf-8'))
def decrypt_file(encrypted_path: str, output_path: str,
encryption_metadata: dict, api_secret: str):
"""Decrypt a file using API secret"""
# Derive user key
user_key = derive_user_key(api_secret)
# Decrypt file key from metadata
encrypted_file_key = base64.b64decode(encryption_metadata['encrypted_key'])
aesgcm = AESGCM(user_key)
file_key = aesgcm.decrypt(
nonce=b'file_key_iv_16b',
data=encrypted_file_key,
associated_data=None
)
# Read and decrypt file
with open(encrypted_path, 'rb') as f:
file_data = f.read()
file_iv = file_data[:12]
ciphertext = file_data[12:]
file_aesgcm = AESGCM(file_key)
plaintext = file_aesgcm.decrypt(
nonce=file_iv,
data=ciphertext,
associated_data=None
)
with open(output_path, 'wb') as f:
f.write(plaintext)
# Submit a job
response = requests.post(
f"{BASE_URL}/jobs/generate",
headers={"Authorization": f"Bearer {API_SECRET}"},
json={
"image_url": "https://example.com/image.jpg",
"style": "cumshot-facial",
"type": "video"
}
)
job = response.json()
print(f"Job ID: {job['job_id']}")
# Poll for completion
import time
while True:
status = requests.get(
f"{BASE_URL}/jobs/{job['job_id']}",
headers={"Authorization": f"Bearer {API_SECRET}"}
).json()
if status['status'] == 'completed':
# Download encrypted file
encrypted_data = requests.get(status['result_url']).content
with open('encrypted.mp4.encrypted', 'wb') as f:
f.write(encrypted_data)
# Decrypt file
decrypt_file(
'encrypted.mp4.encrypted',
'decrypted.mp4',
status['encryption_metadata'],
API_SECRET
)
print("File decrypted successfully!")
break
elif status['status'] == 'failed':
print(f"Error: {status['error_message']}")
break
time.sleep(5)Error Codes
Standard HTTP status codes with JSON error responses.
| Code | Description |
|---|---|
| 401 | Invalid or missing API key |
| 402 | Insufficient credits |
| 403 | API access not approved |
| 404 | Job not found |
| 429 | Rate limit exceeded |
| 500 | Internal server error |
API Access Application
Before using the API, you need to apply for API access through the website. These endpoints help you check your application status. Note: These endpoints use website JWT authentication, not API keys.
Getting Started: If you don't have API access yet, visit the API Application page on the website to submit your application. Once approved, you can create API keys and start using the API.
Rate Limits
Rate limiting protects our infrastructure. Limits vary by endpoint and API key type (production vs sandbox).
Production Keys (pk_live_/sk_live_)
| Endpoint | Rate Limit |
|---|---|
POST /jobs/generate | 100 requests/minute |
POST /faceswap/jobs | 100 requests/minute |
POST /custom-generation/jobs | 100 requests/minute |
GET /jobs/{id} | 600 requests/minute |
GET /jobs | 300 requests/minute |
GET /usage/* | 300 requests/minute |
Other endpoints | 300 requests/minute |
Sandbox Keys (pk_test_/sk_test_)
| Endpoint | Rate Limit |
|---|---|
POST /jobs/generate | 300 requests/minute |
POST /faceswap/jobs | 300 requests/minute |
POST /custom-generation/jobs | 300 requests/minute |
GET /jobs/{id} | 1000 requests/minute |
GET /jobs | 600 requests/minute |
GET /usage/* | 500 requests/minute |
Other endpoints | 1000 requests/minute |
Rate limit headers included in all responses:
X-RateLimit-Limit- Max requests per minute for this endpointX-RateLimit-Remaining- Remaining requests in current windowX-RateLimit-Reset- Unix timestamp when the limit resetsRetry-After- Seconds to wait before retrying (only on 429 responses)