KeyEnvKeyEnv
SDKs & Integrations

Python SDK

Official Python SDK for KeyEnv.

Python SDK

Official Python SDK for KeyEnv with full type annotations.

Installation

pip install keyenv

Or with poetry:

poetry add keyenv

Quick Start

from keyenv import KeyEnv
import os

client = KeyEnv(token=os.environ["KEYENV_TOKEN"])

# Load secrets into os.environ
client.load_env("your-project-id", "production")
print(os.environ["DATABASE_URL"])

Initialize the Client

from keyenv import KeyEnv

client = KeyEnv(
    token="your-service-token",
    timeout=30.0,  # optional, default 30s
)

Context Manager

Use as a context manager for automatic cleanup:

with KeyEnv(token="your-token") as client:
    secrets = client.export_secrets("project-id", "production")

Loading Secrets

Load into os.environ

The simplest way to use secrets in your application:

count = client.load_env("project-id", "production")
print(f"Loaded {count} secrets")

# Now use them
print(os.environ["DATABASE_URL"])
print(os.environ["API_KEY"])

Export as Dictionary

Get secrets as a key-value dictionary:

env = client.export_secrets_as_dict("project-id", "production")
print(env["DATABASE_URL"])
print(env["API_KEY"])

Export as List

Get secrets with metadata:

secrets = client.export_secrets("project-id", "production")
for secret in secrets:
    print(f"{secret.key}={secret.value}")

Managing Secrets

Get a Single Secret

secret = client.get_secret("project-id", "production", "DATABASE_URL")
print(secret.value)
print(secret.description)

Set a Secret

Creates or updates a secret:

client.set_secret("project-id", "production", "API_KEY", "sk_live_...")

# With description
client.set_secret(
    "project-id",
    "production",
    "API_KEY",
    "sk_live_...",
    description="Production API key"
)

Delete a Secret

client.delete_secret("project-id", "production", "OLD_KEY")

Bulk Operations

Bulk Import

Import multiple secrets at once:

from keyenv import BulkSecretItem

result = client.bulk_import(
    "project-id",
    "development",
    [
        BulkSecretItem(key="DATABASE_URL", value="postgres://localhost/mydb"),
        BulkSecretItem(key="REDIS_URL", value="redis://localhost:6379"),
        {"key": "API_KEY", "value": "sk_test_..."},  # Also accepts dicts
    ],
    overwrite=True,
)
print(f"Created: {result.created}, Updated: {result.updated}")

Generate .env File

env_content = client.generate_env_file("project-id", "production")
with open(".env", "w") as f:
    f.write(env_content)

Projects & Environments

List Projects

projects = client.list_projects()
for project in projects:
    print(f"{project.name} ({project.id})")

Get Project Details

project = client.get_project("project-id")
print(f"Project: {project.name}")
for env in project.environments:
    print(f"  - {env.name}")

List Environments

environments = client.list_environments("project-id")
for env in environments:
    print(env.name)

Environment Permissions

Manage who can access secrets in each environment.

List Permissions

permissions = client.list_permissions("project-id", "production")
for perm in permissions:
    print(f"{perm.user_email}: {perm.role}")

Set Permission

# Grant write access to a user
client.set_permission("project-id", "production", "user-id", "write")

Delete Permission

client.delete_permission("project-id", "production", "user-id")

Get My Permissions

permissions, is_team_admin = client.get_my_permissions("project-id")
for perm in permissions:
    print(f"{perm.environment_name}: {perm.role} (can_write: {perm.can_write})")

Bulk Set Permissions

client.bulk_set_permissions("project-id", "production", [
    {"user_id": "user-1", "role": "write"},
    {"user_id": "user-2", "role": "read"},
])

Project Defaults

# Get default permissions
defaults = client.get_project_defaults("project-id")

# Set default permissions for new team members
client.set_project_defaults("project-id", [
    {"environment_name": "development", "default_role": "write"},
    {"environment_name": "staging", "default_role": "read"},
    {"environment_name": "production", "default_role": "none"},
])

Error Handling

from keyenv import KeyEnv, KeyEnvError

try:
    secret = client.get_secret("project-id", "production", "MISSING_KEY")
except KeyEnvError as e:
    print(f"Error {e.status}: {e.message}")

    if e.status == 401:
        print("Invalid or expired token")
    elif e.status == 403:
        print("Access denied")
    elif e.status == 404:
        print("Secret not found")

Type Hints

The SDK includes full type annotations for better IDE support:

from keyenv import Secret, SecretWithValue, Project, Environment

API Reference

Constructor Parameters

ParameterTypeRequiredDefaultDescription
tokenstrYes-Service token
timeoutfloatNo30.0Request timeout (seconds)

Methods

MethodDescription
get_current_user()Get current user/token info
list_projects()List all accessible projects
get_project(id)Get project with environments
list_environments(project_id)List environments in a project
list_secrets(project_id, env)List secret keys (no values)
export_secrets(project_id, env)Export secrets with values
export_secrets_as_dict(project_id, env)Export as dictionary
get_secret(project_id, env, key)Get single secret
set_secret(project_id, env, key, value)Create or update secret
delete_secret(project_id, env, key)Delete secret
bulk_import(project_id, env, secrets)Bulk import secrets
load_env(project_id, env)Load secrets into os.environ
generate_env_file(project_id, env)Generate .env file content
list_permissions(project_id, env)List permissions for an environment
set_permission(project_id, env, user_id, role)Set user's permission
delete_permission(project_id, env, user_id)Delete user's permission
bulk_set_permissions(project_id, env, permissions)Bulk set permissions
get_my_permissions(project_id)Get current user's permissions
get_project_defaults(project_id)Get default permissions
set_project_defaults(project_id, defaults)Set default permissions

Examples

FastAPI Application

from fastapi import FastAPI
from keyenv import KeyEnv
import os

# Load secrets at startup
client = KeyEnv(token=os.environ["KEYENV_TOKEN"])
client.load_env(os.environ["KEYENV_PROJECT"], "production")

app = FastAPI()

@app.get("/")
async def root():
    return {"status": "ok"}

@app.get("/config")
async def config():
    return {
        "api_url": os.environ.get("API_URL"),
        # Don't expose sensitive secrets!
    }

Django Settings

# settings.py
from keyenv import KeyEnv
import os

# Load secrets before Django initializes
if os.environ.get("KEYENV_TOKEN"):
    client = KeyEnv(token=os.environ["KEYENV_TOKEN"])
    client.load_env(os.environ["KEYENV_PROJECT"], os.environ.get("ENV", "development"))

# Now use secrets in settings
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "HOST": os.environ.get("DB_HOST"),
        "NAME": os.environ.get("DB_NAME"),
        "USER": os.environ.get("DB_USER"),
        "PASSWORD": os.environ.get("DB_PASSWORD"),
    }
}

SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY")

Flask Application

from flask import Flask
from keyenv import KeyEnv
import os

# Load secrets
client = KeyEnv(token=os.environ["KEYENV_TOKEN"])
client.load_env(os.environ["KEYENV_PROJECT"], "production")

app = Flask(__name__)
app.config["SECRET_KEY"] = os.environ["FLASK_SECRET_KEY"]

@app.route("/")
def hello():
    return {"status": "ok"}

Script/CLI Tool

#!/usr/bin/env python3
import argparse
from keyenv import KeyEnv
import os

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--env", default="development")
    args = parser.parse_args()

    client = KeyEnv(token=os.environ["KEYENV_TOKEN"])
    secrets = client.export_secrets_as_dict("my-project", args.env)

    print(f"Database: {secrets['DATABASE_URL']}")
    # Do something with secrets...

if __name__ == "__main__":
    main()

On this page