Use SupaHost with Python

Use supabase-py with a SupaHost project. Install the client, query and insert rows, sign users in, run admin scripts with the service_role key, verify users in FastAPI and use the async client.

supabase-py is the official Python client. It talks to your project over HTTPS through the same Data API, Auth, Storage and Functions endpoints the JavaScript client uses, so it works with SupaHost with no changes and no database connection. That matters on SupaHost, where Postgres isn't reachable from outside the instance except through the Business plan's SSH tunnel.

The examples use the recipes table from the quickstart.

Install

python -m venv .venv
source .venv/bin/activate
pip install supabase

Add your project keys

Take the Project URL and keys from the project's Connect tab (subtab App) and export them:

export SUPABASE_URL=https://app-one.acme.supahost.space
export SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIs...
# Only for trusted server code and scripts:
export SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIs...

Use the anon key for anything that acts on behalf of a user. The service_role key bypasses Row Level Security; keep it in server-side secrets.

Query and insert

import os
from supabase import create_client

supabase = create_client(os.environ["SUPABASE_URL"], os.environ["SUPABASE_ANON_KEY"])

response = (
    supabase.table("recipes")
    .select("id, title, minutes")
    .lte("minutes", 30)
    .order("created_at", desc=True)
    .execute()
)
for recipe in response.data:
    print(recipe["title"], recipe["minutes"])

Failed requests raise an exception carrying Postgres's error code and message:

from postgrest.exceptions import APIError

try:
    supabase.table("recipes").insert({"title": "Lemon rice", "minutes": 30}).execute()
except APIError as err:
    print(err.code, err.message)  # 42501 for a Row Level Security violation

This insert fails while signed out, because the quickstart's insert policy is for authenticated users.

Sign in

supabase.auth.sign_up({"email": "[email protected]", "password": "a-long-password"})

session = supabase.auth.sign_in_with_password(
    {"email": "[email protected]", "password": "a-long-password"}
)
print(session.user.id)

# Requests on this client now carry the user's token.
supabase.table("recipes").insert({"title": "Lemon rice", "minutes": 30}).execute()

supabase.auth.sign_out()

Admin scripts with the service role

For back-office jobs, such as creating users or fixing data, create a separate client with the service_role key. It skips Row Level Security and can call the Auth admin API:

import os
from supabase import create_client

admin = create_client(os.environ["SUPABASE_URL"], os.environ["SUPABASE_SERVICE_ROLE_KEY"])

admin.auth.admin.create_user(
    {"email": "[email protected]", "password": "a-long-password", "email_confirm": True}
)

old = admin.table("recipes").delete().lt("created_at", "2024-01-01").execute()
print(f"removed {len(old.data)} rows")

Verify users in an API (FastAPI)

When your frontend calls a Python API, send the user's access token in the Authorization header. The API checks it with Auth and then queries as that user, so policies still apply:

import os
from fastapi import FastAPI, Header, HTTPException
from supabase import create_client, ClientOptions

URL = os.environ["SUPABASE_URL"]
ANON = os.environ["SUPABASE_ANON_KEY"]

app = FastAPI()

@app.get("/my-recipes")
def my_recipes(authorization: str = Header(...)):
    token = authorization.removeprefix("Bearer ").strip()
    client = create_client(
        URL, ANON, options=ClientOptions(auto_refresh_token=False, persist_session=False)
    )
    try:
        user = client.auth.get_user(token).user
    except Exception:
        raise HTTPException(status_code=401, detail="invalid token")

    client.postgrest.auth(token)  # run queries as this user
    rows = client.table("recipes").select("id, title").eq("user_id", user.id).execute()
    return rows.data

Create a client per request, as above. A shared client would mix up users.

The async client

For asyncio apps, use acreate_client:

import asyncio, os
from supabase import acreate_client

async def main():
    supabase = await acreate_client(os.environ["SUPABASE_URL"], os.environ["SUPABASE_ANON_KEY"])
    res = await supabase.table("recipes").select("id, title").limit(5).execute()
    print(res.data)

asyncio.run(main())

Edge Functions and Storage

The same client calls your other services:

result = supabase.functions.invoke("estimate-cost", invoke_options={"body": {"recipe_id": 1}})
files = supabase.storage.from_("photos").list()

SQLAlchemy, Django and psycopg

ORMs and drivers that open a Postgres connection need direct database access. On SupaHost that's only available on Business and Enterprise, through the SSH tunnel, and it's meant for tools on a developer machine, such as running migrations. For application code on other plans, use supabase-py as shown here. See Using SQLAlchemy with Supabase Postgres.

Next steps

Updated 11 November 2026 · View as Markdown · Something wrong or missing? Tell us