Complete technical reference for the Dither Magic REST API.
- Development:
http://localhost:5000 - Production: Your deployment URL
Currently, the API does not require authentication. For production deployments, consider implementing:
- API keys
- Rate limiting
- IP allowlisting
Process an image using a specified dithering algorithm.
Content-Type: multipart/form-data
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
file |
File | Yes | Image file to process (PNG, JPEG, GIF, WebP) |
algorithm |
String | No | Dithering algorithm (default: floyd-steinberg) |
Supported Algorithms:
| Value | Algorithm Name |
|---|---|
floyd-steinberg |
Floyd-Steinberg (default) |
atkinson |
Atkinson |
stucki |
Stucki |
jarvis |
Jarvis-Judice-Ninke |
burkes |
Burkes |
sierra |
Sierra (3-Row) |
sierra-two-row |
Sierra Two-Row |
sierra-lite |
Sierra Lite |
ordered |
Ordered Dithering |
bayer |
Bayer |
halftone |
Halftone |
blue-noise |
Blue Noise |
File Constraints:
- Maximum size: 32MB
- Supported formats: PNG, JPEG, GIF, WebP
- Recommended size: Under 5MB for optimal performance
Success (200 OK):
- Content-Type:
image/png - Body: Binary PNG image data
Error Responses:
| Status Code | Error | Description |
|---|---|---|
| 400 | No file part |
Request missing file |
| 400 | No selected file |
File field is empty |
| 400 | Invalid file type |
Unsupported image format |
| 400 | Invalid algorithm |
Algorithm name not recognized |
| 400 | Invalid or corrupted image file |
File cannot be opened as an image |
| 413 | Request entity too large |
File exceeds 32MB limit |
| 500 | Various | Server error during processing |
Error Response Format:
{
"error": "Error description"
}cURL:
curl -X POST \
-F "file=@image.jpg" \
-F "algorithm=floyd-steinberg" \
http://localhost:5000/api/dither \
-o output.pngPython (requests):
import requests
url = 'http://localhost:5000/api/dither'
files = {'file': open('image.jpg', 'rb')}
data = {'algorithm': 'atkinson'}
response = requests.post(url, files=files, data=data)
if response.status_code == 200:
with open('output.png', 'wb') as f:
f.write(response.content)JavaScript (Fetch API):
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('algorithm', 'floyd-steinberg');
const response = await fetch('http://localhost:5000/api/dither', {
method: 'POST',
body: formData
});
const blob = await response.blob();Node.js:
const FormData = require('form-data');
const fs = require('fs');
const fetch = require('node-fetch');
const formData = new FormData();
formData.append('file', fs.createReadStream('image.jpg'));
formData.append('algorithm', 'atkinson');
const response = await fetch('http://localhost:5000/api/dither', {
method: 'POST',
body: formData
});
const buffer = await response.buffer();
fs.writeFileSync('output.png', buffer);The API currently does not enforce rate limiting.
- Per-IP limit: 100 requests per minute
- Per-endpoint limit: 50 requests per minute
- Concurrent requests: 10 per IP
Use Flask-Limiter for basic rate limiting:
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(
app=app,
key_func=get_remote_address,
default_limits=["100 per minute"]
)
@app.route('/api/dither', methods=['POST'])
@limiter.limit("50 per minute")
def dither_api():
# Implementation
passApproximate processing times by image size and algorithm:
| Image Size | Fast Algorithms* | Medium Algorithms** | Slow Algorithms*** |
|---|---|---|---|
| < 1MB | 1-2s | 2-4s | 3-6s |
| 1-5MB | 3-5s | 5-10s | 10-20s |
| 5-10MB | 10-15s | 15-30s | 30-60s |
| 10-32MB | 20-40s | 40-90s | 90-180s |
* Fast: ordered, bayer, sierra-lite ** Medium: floyd-steinberg, atkinson, burkes, sierra-two-row *** Slow: stucki, jarvis, sierra, halftone, blue-noise
- Resize images client-side before uploading
- Use faster algorithms for real-time or preview use cases
- Implement caching for repeated requests
- Use async processing for batch operations
- Compress images before uploading
Memory usage scales with image dimensions:
| Image Dimensions | Approximate Memory |
|---|---|
| 1000x1000 | ~12 MB |
| 2000x2000 | ~48 MB |
| 4000x4000 | ~192 MB |
| 6000x6000 | ~432 MB |
Always implement proper error handling in your client code:
import requests
from requests.exceptions import RequestException
def dither_image_safe(image_path, algorithm='floyd-steinberg'):
url = 'http://localhost:5000/api/dither'
try:
with open(image_path, 'rb') as f:
files = {'file': f}
data = {'algorithm': algorithm}
response = requests.post(url, files=files, data=data, timeout=60)
response.raise_for_status()
return response.content
except FileNotFoundError:
print(f"File not found: {image_path}")
except requests.exceptions.HTTPError as e:
if e.response.status_code == 400:
error = e.response.json().get('error', 'Bad request')
print(f"Bad request: {error}")
elif e.response.status_code == 413:
print("File too large (max 32MB)")
else:
print(f"HTTP error {e.response.status_code}")
except requests.exceptions.Timeout:
print("Request timed out")
except RequestException as e:
print(f"Network error: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
return NoneFor production use, implement exponential backoff for transient errors:
import time
import requests
def dither_with_retry(image_path, algorithm='floyd-steinberg', max_retries=3):
url = 'http://localhost:5000/api/dither'
for attempt in range(max_retries):
try:
with open(image_path, 'rb') as f:
files = {'file': f}
data = {'algorithm': algorithm}
response = requests.post(url, files=files, data=data, timeout=60)
response.raise_for_status()
return response.content
except requests.exceptions.RequestException as e:
if attempt < max_retries - 1:
wait_time = 2 ** attempt # Exponential backoff: 1s, 2s, 4s
print(f"Attempt {attempt + 1} failed, retrying in {wait_time}s...")
time.sleep(wait_time)
else:
print(f"All {max_retries} attempts failed")
raise
return NoneFor cross-origin requests, configure CORS in Flask:
from flask_cors import CORS
# Allow all origins (development only)
CORS(app)
# Or configure specific origins (production)
CORS(app, origins=[
"https://yourdomain.com",
"https://app.yourdomain.com"
])Install flask-cors:
pip install flask-corsFor processing multiple images, use parallel requests:
import aiohttp
import asyncio
async def dither_batch(image_paths, algorithm='floyd-steinberg'):
async with aiohttp.ClientSession() as session:
tasks = []
for path in image_paths:
task = dither_async(session, path, algorithm)
tasks.append(task)
results = await asyncio.gather(*tasks, return_exceptions=True)
return results
async def dither_async(session, image_path, algorithm):
url = 'http://localhost:5000/api/dither'
with open(image_path, 'rb') as f:
data = aiohttp.FormData()
data.add_field('file', f, filename=image_path)
data.add_field('algorithm', algorithm)
async with session.post(url, data=data) as response:
if response.status == 200:
return await response.read()
else:
error = await response.json()
raise Exception(error.get('error'))
# Usage
results = asyncio.run(dither_batch(['img1.jpg', 'img2.jpg', 'img3.jpg']))async function ditherBatch(files, algorithm = 'floyd-steinberg') {
const promises = files.map(file => ditherImage(file, algorithm));
const results = await Promise.allSettled(promises);
return results.map((result, index) => ({
file: files[index].name,
success: result.status === 'fulfilled',
data: result.status === 'fulfilled' ? result.value : null,
error: result.status === 'rejected' ? result.reason : null
}));
}
async function ditherImage(file, algorithm) {
const formData = new FormData();
formData.append('file', file);
formData.append('algorithm', algorithm);
const response = await fetch('http://localhost:5000/api/dither', {
method: 'POST',
body: formData
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error);
}
return await response.blob();
}Future versions may support webhooks for async processing:
POST /api/dither
{
"file_url": "https://example.com/image.jpg",
"algorithm": "floyd-steinberg",
"webhook_url": "https://yourapp.com/webhook"
}
// Webhook callback
POST https://yourapp.com/webhook
{
"status": "completed",
"result_url": "https://api.dither-magic.com/results/abc123.png",
"processing_time": 3.24
}- API Examples - Practical code examples
- Algorithm Reference - Detailed algorithm descriptions
- Troubleshooting - Common issues and solutions
For API support:
- Open an issue on GitHub
- Check existing documentation
- Review API examples