Serve a dynamic SVG badge

In this example, we use Modal’s webhook capability to host a dynamic SVG badge that shows you the current # of downloads for a Python package.

First let’s start off by creating a Modal stub, and defining an image with the Python packages we’re going to be using:

import modal

stub = modal.Stub(
    image=modal.Image.debian_slim().pip_install(["pybadges", "pypistats"]),

Defining the webhook

Instead of using @stub.function to decorate our function, we use the @modal.webhook decorator ( learn more), which instructs Modal to create a REST endpoint that serves this function. Note that the default method is GET, but this can be overridden using the method argument.

async def package_downloads(package_name: str):
    import json

    import pypistats
    from fastapi import Response
    from pybadges import badge

    stats = json.loads(pypistats.recent(package_name, format="json"))
    svg = badge(
        left_text=f"{package_name} downloads",

    return Response(content=svg, media_type="image/svg+xml")

In this function, we use pypistats to query the most recent stats for our package, and then use that as the text for a SVG badge, rendered using pybadges. Since Modal webhooks are FastAPI functions under the hood, we return this SVG wrapped in a FastAPI response with the correct media type. Also note that FastAPI automatically interprets package_name as a query param.

Running and deploying

We can now run this function as follows:

if __name__ == "__main__":

You can run this script and it will create a short-lived web url that exists until you terminate the script.

If you want to create a persistent URL, you have to deploy the script. To deploy using the Modal CLI by running modal app deploy,

Either way, as soon as we run this command, Modal gives us the link to our brand new webhook in the output:

web badge deployment

We can now visit the link using a web browser, using a package_name of our choice in the URL query params. For example:


The raw source code for this example can be found on GitHub.