Skip to content
ricochet

Python

This page covers the requirements and internals of deploying Python content to ricochet.

Every Python deployment requires two files in the project root:

FilePurpose
.python-versionSpecifies the target Python version (e.g. 3.12 or 3.12.7).
uv.lockThe lockfile generated by uv lock.

The version in .python-version must satisfy the requires-python constraint recorded in uv.lock.

[content]
name = "My FastAPI App"
entrypoint = "app.py"
access_type = "external"
content_type = "fast-api"
[language]
name = "python"
packages = "uv.lock"
[serve]
min_instances = 0
max_instances = 5

Python content items can be deployed as apps or tasks.

Content TypeDescription
fast-apiAn API developed using FastAPI. Launched with uvicorn.
flaskA web app or API developed using Flask.
streamlitA data app developed using Streamlit.
dashA data app developed using Dash.
shiny-pyA web app developed using Shiny for Python.
python-serviceA generic Python web app or API. The entrypoint script is run directly; it must bind to PORT from the environment.
Content TypeDescription
pythonA Python script with the extension .py.
quarto-pyA Quarto document using a Python runtime. Can be served as a static site.

Ricochet resolves the uv binary in the following order:

  1. Vendored$RICOCHET_HOME/vendor/py/uv (installed with the ricochet package)
  2. System PATH — falls back to uv on PATH if the vendored binary is not present

Ricochet discovers Python installations on the host machine. We recommend installing Python versions via uv for the best experience:

Terminal window
uv python install 3.12

When a deployment is received, ricochet matches the version in .python-version against available installations and selects the closest match.

When a bundle is deployed, ricochet restores the Python environment using:

Terminal window
uv sync --python <matched-python> --cache-dir $RICOCHET_HOME/.cache/uv --no-python-downloads --frozen

The --frozen flag ensures the lockfile is used exactly as provided — no resolution or updates occur on the server. The --no-python-downloads flag prevents uv from downloading Python versions at deploy time; they must already be installed on the host.

Resolved packages are cached at $RICOCHET_HOME/.cache/uv. This directory is created automatically during the first Python deployment. The cache is shared across all Python content items on the server.

You can deploy and manage Python content programmatically using the ricochet REST API. The examples below use requests. API keys are created in the ricochet UI under Credentials → API Keys.

Bundle your project as a .tar.gz archive and upload it with the _ricochet.toml config.

import os
import tarfile
import tempfile
import requests
server = "https://try.ricochet.rs"
api_key = os.environ["RICOCHET_API_KEY"]
headers = {"Authorization": f"Key {api_key}"}
# Create a tar.gz bundle of the project directory
bundle_path = tempfile.mktemp(suffix=".tar.gz")
with tarfile.open(bundle_path, "w:gz") as tar:
tar.add(".", arcname=".")
# Deploy new content
resp = requests.post(
f"{server}/api/v0/content/upload",
headers=headers,
files={
"bundle": ("bundle.tar.gz", open(bundle_path, "rb"), "application/gzip"),
"config": ("_ricochet.toml", open("_ricochet.toml", "rb"), "application/toml"),
},
)
result = resp.json()
result["id"]
#> "01JSZAXZ3TSTAYXP56ARDVFJCJ"

To redeploy existing content, pass id instead of config:

resp = requests.post(
f"{server}/api/v0/content/upload",
headers=headers,
files={"bundle": ("bundle.tar.gz", open(bundle_path, "rb"), "application/gzip")},
data={"id": "01JSZAXZ3TSTAYXP56ARDVFJCJ"},
)
content_id = "01JSZAXZ3TSTAYXP56ARDVFJCJ"
resp = requests.post(
f"{server}/api/v0/content/{content_id}/invoke",
headers=headers,
json={"params": {"n": 100}},
)
resp.json()
resp = requests.get(
f"{server}/api/v0/user/items",
headers=headers,
)
items = resp.json()