Get the FREE Ultimate OpenClaw Setup Guide →

Web Endpoints

Scanned
npx machina-cli add skill samarth777/modal-skills/web-endpoints --openclaw
Files (1)
SKILL.md
6.2 KB

Modal Web Endpoints Reference

Detailed reference for creating web APIs and endpoints on Modal.

Endpoint Types

FastAPI Endpoint (Recommended)

Simple function-based endpoints:

import modal

app = modal.App("web-api")
image = modal.Image.debian_slim().pip_install("fastapi[standard]")

@app.function(image=image)
@modal.fastapi_endpoint()
def hello(name: str = "World") -> dict:
    return {"message": f"Hello, {name}!"}

@app.function(image=image)
@modal.fastapi_endpoint(method="POST", docs=True)
def process(body: dict) -> dict:
    return {"received": body}

ASGI App (Full FastAPI/Starlette)

For complex applications with multiple routes:

@app.function(image=image)
@modal.asgi_app()
def fastapi_app():
    from fastapi import FastAPI, HTTPException
    
    web_app = FastAPI()
    
    @web_app.get("/")
    def root():
        return {"status": "ok"}
    
    @web_app.get("/items/{item_id}")
    def get_item(item_id: int):
        return {"item_id": item_id}
    
    @web_app.post("/items")
    def create_item(item: dict):
        return {"created": item}
    
    return web_app

WSGI App (Flask/Django)

@app.function(image=modal.Image.debian_slim().pip_install("flask"))
@modal.wsgi_app()
def flask_app():
    from flask import Flask, jsonify
    
    web_app = Flask(__name__)
    
    @web_app.route("/")
    def index():
        return jsonify({"status": "ok"})
    
    return web_app

Custom Web Server

For non-Python web servers or custom setups:

@app.function()
@modal.web_server(port=8080)
def custom_server():
    import subprocess
    # Start any web server on port 8080
    subprocess.run(["python", "-m", "http.server", "8080"])

Endpoint Configuration

HTTP Methods

@modal.fastapi_endpoint(method="GET")      # Default
@modal.fastapi_endpoint(method="POST")
@modal.fastapi_endpoint(method="PUT")
@modal.fastapi_endpoint(method="DELETE")

Documentation

# Enable OpenAPI docs at /docs
@modal.fastapi_endpoint(docs=True)
def documented_endpoint():
    ...

Custom Labels

# Custom URL label
@modal.fastapi_endpoint(label="my-api")
def endpoint():
    ...
# URL: https://workspace--my-api.modal.run

Custom Domains

@modal.fastapi_endpoint(custom_domains=["api.example.com", "api.example.net"])
def multi_domain_endpoint():
    ...

Proxy Authentication

# Require Modal proxy auth tokens
@modal.fastapi_endpoint(requires_proxy_auth=True)
def private_endpoint():
    return {"secret": "data"}

Call with:

curl -H "Modal-Key: $TOKEN_ID" \
     -H "Modal-Secret: $TOKEN_SECRET" \
     https://your-endpoint.modal.run

Streaming Responses

Server-Sent Events (SSE)

from fastapi.responses import StreamingResponse

@app.function(image=image)
@modal.fastapi_endpoint()
async def stream():
    async def generate():
        for i in range(10):
            yield f"data: {i}\n\n"
            await asyncio.sleep(0.1)
    
    return StreamingResponse(generate(), media_type="text/event-stream")

Streaming with Map

@app.function(gpu="A100")
def process_chunk(chunk: str) -> str:
    return f"processed: {chunk}"

@app.function(image=image)
@modal.fastapi_endpoint()
async def stream_processing(body: dict):
    from fastapi.responses import StreamingResponse
    
    chunks = body["chunks"]
    
    async def generate():
        async for result in process_chunk.map.aio(chunks):
            yield f"data: {result}\n\n"
    
    return StreamingResponse(generate(), media_type="text/event-stream")

Concurrency

Handle Multiple Requests

@app.function()
@modal.concurrent(max_inputs=20)  # 20 concurrent requests per container
@modal.asgi_app()
def high_concurrency_app():
    ...

URL Structure

Auto-generated URLs follow this pattern:

https://<workspace>-<env-suffix>--<app>-<function>.modal.run

Example: https://myworkspace-prod--my-app-hello.modal.run

Ephemeral Apps

Apps run with modal serve get a -dev suffix:

https://myworkspace-prod--my-app-hello-dev.modal.run

Request/Response Handling

Request Bodies

from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float

@app.function(image=image)
@modal.fastapi_endpoint(method="POST")
def create_item(item: Item) -> dict:
    return {"created": item.dict()}

File Uploads

from fastapi import UploadFile, File

@app.function(image=image)
@modal.fastapi_endpoint(method="POST")
async def upload(file: UploadFile = File(...)):
    contents = await file.read()
    return {"filename": file.filename, "size": len(contents)}

Custom Responses

from fastapi.responses import JSONResponse, HTMLResponse

@app.function(image=image)
@modal.fastapi_endpoint()
def custom_response():
    return JSONResponse(
        content={"message": "hello"},
        headers={"X-Custom-Header": "value"}
    )

Long-Running Requests

Timeouts

Web endpoints have a 150-second HTTP timeout, but auto-redirect for longer tasks.

Job Queue Pattern

For very long tasks, use spawn + polling:

@app.function()
def long_task(data: dict) -> dict:
    import time
    time.sleep(300)  # 5 minutes
    return {"result": "done"}

@app.function(image=image)
@modal.asgi_app()
def api():
    from fastapi import FastAPI
    web_app = FastAPI()
    
    @web_app.post("/submit")
    async def submit(data: dict):
        call = long_task.spawn(data)
        return {"call_id": call.object_id}
    
    @web_app.get("/result/{call_id}")
    async def result(call_id: str):
        call = modal.FunctionCall.from_id(call_id)
        try:
            return call.get(timeout=0)
        except TimeoutError:
            return {"status": "pending"}, 202
    
    return web_app

Deployment

# Development with hot-reload
modal serve app.py

# Production deployment
modal deploy app.py

Getting Endpoint URL

@app.function(image=image)
@modal.fastapi_endpoint()
def my_endpoint():
    # Get own URL
    url = my_endpoint.get_web_url()
    return {"url": url}

# From external code
fn = modal.Function.from_name("my-app", "my_endpoint")
url = fn.get_web_url()

Source

git clone https://github.com/samarth777/modal-skills/blob/main/skills/web-endpoints/SKILL.mdView on GitHub

Overview

Detailed reference for deploying web APIs on Modal, including FastAPI endpoints, ASGI/WSGI apps, and custom web servers. It covers configuring HTTP methods, docs, labels, custom domains, proxy auth, streaming, and concurrency to make APIs production-ready on Modal.

How This Skill Works

You annotate your Modal function with the appropriate decorator to expose it as a web endpoint. Choose fastapi_endpoint for simple function-based routes, asgi_app for multi-route FastAPI/Starlette apps, wsgi_app for Flask/Django, or web_server for non-Python servers. Modal then handles hosting and exposes a stable URL, with optional OpenAPI docs and security options.

When to Use It

  • Expose a small, function-based API using FastAPI endpoints
  • Share a multi-route application via an ASGI app
  • Wrap a Flask/Django app with a WSGI app
  • Run a non-Python or custom web server under Modal
  • Require private access with proxy authentication or custom domains

Quick Start

  1. Step 1: Create a Modal app and pick an image (e.g., debian_slim) with necessary packages
  2. Step 2: Decorate your function with @modal.fastapi_endpoint(), or use @modal.asgi_app() / @modal.wsgi_app()
  3. Step 3: Deploy and call the endpoint URL, optionally enabling docs and custom labels

Best Practices

  • Prefer fastapi_endpoint for simple endpoints that don’t require routing complexity
  • Use asgi_app for larger apps with multiple routes
  • Enable OpenAPI docs when documenting APIs
  • Leverage custom labels and domains for friendly, stable URLs
  • Protect private endpoints with requires_proxy_auth and validate via curl

Example Use Cases

  • Hello endpoint returning a message via a FastAPI function
  • POST endpoint that echoes received data
  • ASGI app with multiple routes for root and items
  • Flask-based WSGI app exposed as a web endpoint
  • Private endpoint secured with Modal proxy authentication

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers