How to keep API keys out of your Python scripts
Almost every useful automation needs a key: a Stripe key, a database URL, a CRM token. The quick way to make a script work is to paste the key into it, and that's how keys end up in git history, shared notebooks and Slack threads.
Callables keeps keys out of your code and away from the people who run your functions.
Declare what the function needs
List the environment variables a function needs in secrets, and read them with os.environ:
import os
import stripe
from callables import deploy
@deploy(title="Refund an order", secrets=["STRIPE_KEY"])
def refund(charge_id: str) -> str:
"""Refunds a charge in full."""
stripe.api_key = os.environ["STRIPE_KEY"]
stripe.Refund.create(charge=charge_id)
return f"Refunded {charge_id}."
The code only names the secret. The value never appears in your repo.
Set the value once
After you push, open the project in the web app and set STRIPE_KEY. Values are stored encrypted and passed to the function as environment variables each time it runs.
The people who run the function never see the value. They see a form and a result. If a declared secret hasn't been set yet, the function still runs without it, and builders see a "missing secrets" warning on the project so it's easy to spot.
Share secrets across related functions
When several functions use the same keys, put them in a group and declare the secrets once:
from callables import Group
billing = Group("billing", title="Billing", secrets=["STRIPE_KEY"])
@billing.deploy(title="Refund an order")
def refund(charge_id: str) -> str: ...
@billing.deploy(title="Find a customer")
def find_customer(email: str) -> list[dict]: ...
Every action in the group gets STRIPE_KEY, and each can declare more of its own. See groups.
Optional secrets and key files
- Optional secrets: declare it, then read it with
os.environ.get("NAME")and handle it being missing. - Key files, like a
.pemfile or a service account JSON: each secret is a single value, so paste the file's contents as the secret, and have your code read it from the variable.
Testing locally
On your own machine, the same variables come from your environment, so a .env file works well for call local. Keep .env in .gitignore.
Rules worth keeping
- Never hard-code a key, even "just for testing".
- Never take a key as a form input. Anyone running the function would have to know it.
- Give each automation the narrowest key that works, like a restricted Stripe key, rather than an admin key.
Turn your scripts into tools your team can run
Add @deploy to a Python function and push. Your team gets a form, schedules and run history.