Deployed personal service

Image Effects API

Backend development & tooling · 2026

A self-hosted image-processing API for Discord bot integrations, with GIF support, ordered effects, per-client keys, and a private testing dashboard.

Python Flask Pillow SQLite Docker Caddy REST API

Problem

A friend wanted an API they could call from a Discord bot. I already ran self-hosted services, but sharing access to an entire server dashboard would expose more than the integration needed.

I built a focused image-processing service: upload an image, choose captions or effects, and receive a PNG or animated GIF. The bot can handle its own commands and messages while the API owns rendering, validation, and access control.

My role

I implemented the Python service, rendering pipeline, key-management interface, browser tester, and integration documentation. I deployed it as a separate Docker service behind my existing reverse proxy. The API documentation includes HTTP examples and Java/JDA integration examples; the Discord bot itself is a separate client, outside this project’s scope.

Approach

The service started with captions and grew into an ordered effects pipeline. Clients can submit up to four steps with individual intensities. A caption step is allowed only at the end, keeping text readable after effects such as distortion, deepfry, or shake. The API enforces the same rules as the private browser tester.

Animated uploads required more than looping over images. The renderer preserves frame timing, loop behavior, and transparent backgrounds for supported transformations. GIF encoding and disposal handling are covered by tests so moving content does not leave trails behind it. A separate explosion template uses the first uploaded frame as its introduction; it does not preserve the source animation.

Architecture

  1. API client: sends a multipart image upload, effect options, and a bearer key over HTTPS. The service accepts uploaded files rather than fetching arbitrary remote image URLs.
  2. Caddy reverse proxy: routes public API traffic separately from the protected administration interface.
  3. Flask service: validates the key and request, applies a per-key rate limit, and admits work only when a rendering slot is available.
  4. Pillow pipeline: decodes the upload, processes frames in effect order, adds the final caption, and returns PNG or GIF bytes.
  5. SQLite key store: records key digests, revocation state, successful request counts, and last use. Full keys are returned only at creation.

The private dashboard creates and revokes client keys, displays usage, and previews requests. The rendering service runs under Gunicorn in Docker as a non-root user, with persistent key data stored separately from the image.

Engineering decisions

Outcome and validation

The API is deployed with public integration documentation and a private tester. It provides a reusable backend for image features without requiring clients to install or maintain the rendering stack.

The automated suite covers key creation and revocation, invalid requests, effect ordering, final-step captions, GIF timing, transparency, and animation limits. The container build runs that suite, and deployment checks exercised live rendering endpoints during development.

This is a personal service built for a small integration audience. It brings client authentication, binary image output, and operational tooling into one complete workflow. The main lesson was making a playful feature dependable for another developer: clear input rules, bounded processing, predictable errors, and documentation they can use directly.

← back to sokolov.software