Skip to main content

End-to-end encrypted environment variables for teams. Manage workspaces, projects, environments and secrets from code.

Website • Dashboard • Documentation • Services Status


Intro to the Python Package

The official Python client for the Envless API. It has every method of the TypeScript SDK, all 113 across 13 namespaces, under the same names in snake_case and typed end to end. There is a synchronous client and an asynchronous one with the same methods. It runs on Python 3.10 and newer and depends on httpx, anyio, cryptography and typing-extensions.

It carries an API key with write access, so it belongs on a server, in a script or in CI, never in a browser.

Installing

pip install envless-sdk

Or uv add envless-sdk, or poetry add envless-sdk. The package installs as envless-sdk and imports as envless; the plain envless name on PyPI belongs to an unrelated project.

Using

import os

from envless import encrypt_value, envless

passphrase = os.environ['ENVLESS_PASSPHRASE']
workspace_id = envless.me.get()['workspaceId']

envless.variables.create('api', 'production', {
    'name': 'STRIPE_SECRET_KEY',
    'value': encrypt_value('sk_live_51H...', passphrase, workspace_id),
})

envless is a ready made client that reads ENVLESS_TOKEN the first time it is touched. Call init(...) once at startup to configure it, or build your own with Envless(), which reads the same variable when you pass no token.

Values are encrypted on your machine, never by the server, so a plaintext value is refused. Request bodies and responses are plain dictionaries with the API's own field names, so 'defaultValue' and 'updatedAt' read exactly as they do in the API reference. Every body and response has a TypedDict in envless.types, so your editor completes the keys and a type checker catches a misspelt one.

A client of your own

from envless import Envless

with Envless(timeout=10, max_retries=2) as client:
    for variable in client.variables.iterate('api', 'production', product='billing'):
        print(variable['name'], variable['updatedAt'])

A client keeps one connection pool, is safe to share between threads, and closes with client.close() or a with block.

Async

import asyncio

from envless import AsyncEnvless


async def main() -> None:
    async with AsyncEnvless() as client:
        async for variable in client.variables.iterate('api', 'production'):
            print(variable['name'])


asyncio.run(main())

AsyncEnvless has every method Envless has, with the same arguments, and runs on asyncio and trio.

Pagination

Every collection has list for one page, list_all for every page at once and iterate to stream items and stop whenever you like. A page is {'items': [...], 'pagination': {'limit', 'offset', 'totalCount', 'hasMore'}}.

Encryption

import os

from envless import decrypt_with_key, derive_workspace_key, envless

workspace_id = envless.me.get()['workspaceId']
key = derive_workspace_key(os.environ['ENVLESS_PASSPHRASE'], workspace_id)
secrets = {
    variable['name']: decrypt_with_key(key, variable['value'])
    for variable in envless.variables.list_all('api', 'production')
    if variable['value'] is not None
}

encrypt_value and decrypt_value derive the key on every call, which costs 200,000 rounds of PBKDF2. To work with many values, derive the key once with derive_workspace_key, or load the ENVLESS_KEY your CI holds with import_workspace_key, then use encrypt_with_key and decrypt_with_key. A derived key is bound to 200,000 iterations, so an older v2: value pinning another count raises DecryptionError there and needs decrypt_value. A WorkspaceKey never prints its bytes. Ciphertext from this package and from the TypeScript SDK, the CLI and the dashboard is interchangeable.

is_ciphertext, passphrase_strength, variable_name_validation and ENCRYPTION_PARAMETERS work exactly as they do in TypeScript. rotate_workspace_passphrase(client=..., ...) re-encrypts an environment and its version history under a new passphrase, and with an AsyncEnvless you await it.

Errors

from envless import EnvlessApiError, envless

try:
    envless.variables.get('api', 'production', 'MISSING')
except EnvlessApiError as error:
    if error.is_not_found:
        print(error.code, error.request_id)
    else:
        raise

An API refusal is EnvlessApiError, with status, code, resource, field, request_id and retry_after_seconds, plus is_auth, is_scope_missing, is_validation, is_not_found, is_conflict, is_rate_limited and needs_upgrade. No response at all is EnvlessNetworkError, with is_timeout when the deadline passed. A value that cannot be decrypted is DecryptionError. All of them inherit EnvlessError. An unusable variable name is refused before anything is sent, with the same EnvlessApiError the API would answer with.

Webhooks

import os

from fastapi import FastAPI, Request, Response
from envless import verify_webhook_signature

app = FastAPI()


@app.post('/webhooks/envless')
async def webhook(request: Request) -> Response:
    event = verify_webhook_signature(
        payload=await request.body(),
        headers=request.headers,
        secret=os.environ['ENVLESS_WEBHOOK_SECRET'],
    )

    print(event['type'], event['data'])

    return Response(status_code=204)

It checks the signature in constant time and rejects a delivery more than five minutes old, then returns the parsed event, or raises WebhookVerificationError. Pass the raw body as bytes or text, since re-serialising it changes the bytes and the signature will not match. Headers can come from any framework, the lookup ignores case.

Configuring

Envless and AsyncEnvless take token, base_url, timeout, max_retries, http_client, user_agent and disable_update_notice. base_url also comes from ENVLESS_API_ENDPOINT. timeout is in seconds, bounds each attempt including the response body, and 0 turns it off. GET, HEAD, PUT and DELETE requests are retried on 408, 429, 500, 502, 503 and 504, up to three times with exponential backoff, waiting what Retry-After asks for up to 30 seconds. Writes that cannot safely repeat are not retried. Pass an httpx.Client, or an httpx.AsyncClient for the async client, as http_client to route through a proxy. Every method also takes timeout= for that one call.

An endpoint no method wraps yet is one raw.request() away, with the client's key, base URL, timeout and retries applied:

from envless import envless

envless.raw.request('/projects', method='POST', body={'name': 'Billing', 'slug': 'billing'})

When a newer version is on PyPI the client says so once on a terminal. ENVLESS_DISABLE_UPDATE_NOTICE=1 or disable_update_notice=True turns that off.

Metadata

Release files for envless-sdk 0.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for envless-sdk 0.0.2
File Size Uploaded
envless_sdk-0.0.2.tar.gz 45.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for envless-sdk 0.0.2
File Interpreter ABI Platform
envless_sdk-0.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 145.7 kB

Release files / envless_sdk-0.0.2.tar.gz

Download URL envless_sdk-0.0.2.tar.gz
Size 45.5 kB
Tags Source
SHA-256 checksum
How to use checksums
078b1443f19f14175f23f07eeb2430158640cd535143894e43d5a918516000da
BLAKE2b-256 checksum
How to use checksums
cadd4219201bf3b23f0a89829c3498c645b69c226dafbfb81c993270d84c5e4d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / envless_sdk-0.0.2-py3-none-any.whl

Download URL envless_sdk-0.0.2-py3-none-any.whl
Size 100.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
14cda3c442f7b4b214129e9ce9bc4f9dffe04b25b6290aa2e5f8c15486556fed
BLAKE2b-256 checksum
How to use checksums
5369727c53ad50d3e3d2a61413b7c2b7b656d64a7253442aba14a29c1e184b63
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.0.2 This release

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page