DEV Community

Cover image for How I Built a Photo Editing SaaS with Zero ML Code (Just 1 API)
AI Engine
AI Engine

Posted on • Originally published at ai-engine.net

How I Built a Photo Editing SaaS with Zero ML Code (Just 1 API)

I wanted to build a photo editing SaaS. Background removal, face enhancement, anime filters, OCR, content moderation. Each feature normally requires its own ML model, its own GPU, its own API subscription.

Instead, I built the whole thing with one API, one Lambda function, and zero ML code. Twelve AI features, $15/month infrastructure cost, 13x margin on every request.

SaaS dashboard showing AI image generation feature

The 12 Features

Every feature is a single POST request to the same API host. Same key, same auth, same response format.

Feature Endpoint Credits
Remove background /v1/remove-background 1
Blur background /v1/blur-background 1
Replace background /v1/color-background 1
Enhance faces /v1/enhance-face 1
Anime/Ghibli filter /v1/cartoonize 1
Colorize B&W photos /v1/colorize-photo 1
Pencil sketch /v1/photo-to-portrait 1
Face swap /v1/swap-face 1
Extract text (OCR) /v1/ocr 2
Detect objects /v1/objects-detection 2
Face analysis /v1/faceanalysis 2
NSFW check /v1/nsfw-detect 2

All on ai-all-in-one.p.rapidapi.com. One host, one key.

Architecture

SaaS architecture: Frontend to Lambda to AI API

No GPU on your side. The Lambda function is a proxy that checks credits and forwards the request.

The Backend

The entire Lambda handler:

import json, boto3, requests

API_HOST = "ai-all-in-one.p.rapidapi.com"
HEADERS = {"x-rapidapi-key": "YOUR_KEY", "x-rapidapi-host": API_HOST}

COSTS = {
    "remove-bg": (1, "/v1/remove-background"),
    "enhance-face": (1, "/v1/enhance-face"),
    "cartoonize": (1, "/v1/cartoonize"),
    "colorize": (1, "/v1/colorize-photo"),
    "ocr": (2, "/v1/ocr"),
    "detect-objects": (2, "/v1/objects-detection"),
    "nsfw-check": (2, "/v1/nsfw-detect"),
}

table = boto3.resource("dynamodb").Table("user_credits")

def handler(event, context):
    user_id = event["requestContext"]["authorizer"]["user_id"]
    action = event["queryStringParameters"]["action"]
    cost, endpoint = COSTS[action]

    user = table.get_item(Key={"user_id": user_id}).get("Item", {})
    if user.get("credits", 0) < cost:
        return {"statusCode": 402, "body": json.dumps({"error": "Not enough credits"})}

    resp = requests.post(f"https://{API_HOST}{endpoint}",
        headers={**HEADERS, "Content-Type": "application/json"},
        json=json.loads(event["body"]))

    table.update_item(Key={"user_id": user_id},
        UpdateExpression="SET credits = credits - :c",
        ExpressionAttributeValues={":c": cost})

    return {"statusCode": 200, "body": resp.text}
Enter fullscreen mode Exit fullscreen mode

That is the entire backend. Every AI feature goes through the same function.

The Money Math

Your cost per credit (Pro plan $14.99/10K credits): $0.0015
You charge per credit: $0.02
Margin: 13x

Users Requests/mo API cost Revenue Profit
50 free 500 $0 (free tier) $0 -$0.10
10 paid 5,000 $14.99 $99.90 $84.41
100 paid 50,000 $49.99 $999 $944

Ten paying users and you are profitable.

Why This Works

  • One integration: 12 features from 1 API, 1 set of docs, 1 billing dashboard
  • No ML ops: no models to download, update, or host
  • Scales to zero: Lambda + DynamoDB on-demand, pay nothing when idle
  • Ship fast: the backend is ~50 lines of Python

👉 Read the full tutorial with screenshots and architecture diagram

Top comments (0)