A FastAPI REST API service for converting images to ASCII art.
- Image to ASCII conversion - Convert JPEG, PNG, and WebP images to ASCII art
- Configurable output - Adjust output width (50-200 characters) and height factor (0.3-0.7)
- Optional authentication - Enable API key authentication via environment variables
- Rate limiting - 10 requests per minute per IP (configurable)
- Security - File size limits, magic number validation, decompression bomb protection
- Observability - Structured logging (JSON for Grafana Loki), Prometheus metrics, Sentry integration
- Docker-ready - Multi-stage build, non-root user, Kubernetes health checks
# Start the service
docker compose up -d
# Check health
curl http://localhost:8000/health
# Convert an image
curl -X POST http://localhost:8000/api/v1/images/convert \
-F "file=@my-image.jpg" \
-F "width=100" \
-F "height_factor=0.5"# Install dependencies
uv sync
# Run the server
uv run uvicorn app.main:app --reload
# Convert an image (in another terminal)
curl -X POST http://localhost:8000/api/v1/images/convert \
-F "file=@my-image.jpg"POST /api/v1/images/convertParameters:
| Parameter | Type | Required | Default | Range | Description |
|---|---|---|---|---|---|
file |
file | Yes | - | - | Image file (JPEG, PNG, or WebP) |
width |
int | No | 100 | 50-200 | Output width in characters |
height_factor |
float | No | 0.5 | 0.3-0.7 | Aspect ratio correction factor |
Response:
{
"ascii_art": "$$$@@@BBBB...",
"width": 100,
"height": 45,
"height_factor": 0.5,
"original_format": "jpeg"
}Error Responses:
400- Invalid image file413- File too large415- Unsupported file type422- Validation error429- Rate limit exceeded
| Endpoint | Description |
|---|---|
GET /health |
Basic liveness check |
GET /health/ready |
Readiness probe (Kubernetes) |
GET /health/live |
Liveness probe (Kubernetes) |
GET /metricsPrometheus metrics including:
http_requests_total- Total HTTP requestshttp_request_duration_seconds- Request durationimages_converted_total- Total images converted
All configuration is done via environment variables. See .env.example for all options.
| Variable | Default | Description |
|---|---|---|
MAX_FILE_SIZE |
10485760 | Max upload size in bytes (10MB) |
DEFAULT_WIDTH |
100 | Default ASCII output width |
HEIGHT_FACTOR |
0.5 | Default height factor (0.3-0.7) |
AUTH_ENABLED |
false | Enable API key authentication |
API_KEY |
- | API key (required if auth enabled) |
LOG_JSON_FORMAT |
false | Output JSON for Loki (vs console) |
SENTRY_DSN |
- | Sentry DSN for error tracking |
# Set environment variables
AUTH_ENABLED=true
API_KEY=your-secret-api-key
# Then use in requests
curl -X POST http://localhost:8000/api/v1/images/convert \
-H "Authorization: Bearer your-secret-api-key" \
-F "file=@my-image.jpg"# Install with dev dependencies
uv sync --extra dev
# Run linter
uv run ruff check --fix
uv run ruff format
# Run tests
uv run pytest
# Run with coverage
uv run pytest --cov=app --cov-report=term-missing --cov-report=html# Build the image
docker build -t ascii-api .
# Run the container
docker run -p 8000:8000 ascii-apiFor local development with hot reload:
# Start the service with volume mounts and auto-reload
docker compose -f dev-docker-compose.yml up -d
# View logs
docker compose -f dev-docker-compose.yml logs -f
# Stop the service
docker compose -f dev-docker-compose.yml downThe development compose file:
- Mounts the
app/directory for live code reloading - Exposes port 8000
- Runs with
LOG_LEVEL=DEBUGfor verbose logging
The Docker image runs as a non-root user (appuser) for security.
MIT License - see LICENSE file for details.