Python SDK for the Convox API
Project description
Python SDK for the Convox API.
Installation
pip install convox
Quick Start
from convox import ConvoxClient
client = ConvoxClient(
host="console.convox.com",
api_key="your-deploy-key",
rack="org/rack-name", # required for console connections
)
# List apps
for app in client.apps.list():
print(f"{app.name}: {app.status}")
# Get app details
app = client.apps.get("myapp")
print(f"Release: {app.release}, Locked: {app.locked}")
Authentication
For console-managed racks the api_key is a deploy key generated in the
Convox console (Organization > Deploy Keys). Deploy keys are the recommended
auth method for programmatic and SDK usage. The examples below all use deploy
keys.
Three options, in order of precedence:
1. Explicit credentials (recommended)
client = ConvoxClient(
host="console.convox.com",
api_key="your-deploy-key",
rack="org/rack-name", # required for console-managed racks
)
2. Environment variables
Set CONVOX_HOST, CONVOX_PASSWORD (deploy key), and CONVOX_RACK:
client = ConvoxClient.from_env()
3. CLI config file
Reads the auth file written by convox login (~/.config/convox/auth for
v3, ~/.convox/auth for v2). This is a convenience for local development
when you have already authenticated via the CLI -- it reads whatever
credential is stored for the given host, whether that is a CLI session token
or a deploy key you placed there manually.
# Console-managed rack — specify both host and rack
client = ConvoxClient.from_cli_config(
host="console.convox.com", # auth file lookup key
rack="org/rack-name", # rack to target
)
# Direct rack connection (host and rack are the same)
client = ConvoxClient.from_cli_config(rack="rack.example.com")
API Reference
Apps
# List, get, create, delete
apps = client.apps.list()
app = client.apps.get("myapp")
app = client.apps.create("myapp")
client.apps.delete("myapp")
# Update (lock/unlock, set parameters)
client.apps.update("myapp", lock=True)
client.apps.update("myapp", parameters={"BuildCpu": "512"})
# Cancel in-progress operation
client.apps.cancel("myapp")
Builds
builds = client.builds.list("myapp")
build = client.builds.get("myapp", "BABCDEF")
build = client.builds.create("myapp", "https://example.com/source.tgz",
description="v2.0 release")
Releases
releases = client.releases.list("myapp")
release = client.releases.get("myapp", "RABCDEF")
# Promote a release to production
client.releases.promote("myapp", "RABCDEF")
# Rollback to a previous release
client.releases.rollback("myapp", "RPREVIOUS")
Environment Variables
# Get current env
env = client.environment.get("myapp")
print(env) # {"DATABASE_URL": "postgres://...", "SECRET_KEY": "..."}
# Set env vars (creates a new release)
release = client.environment.set("myapp", {"KEY": "value", "OTHER": "val"})
# Unset env vars (creates a new release)
release = client.environment.unset("myapp", "KEY")
Services
services = client.services.list("myapp")
client.services.update("myapp", "web", count=3, memory=1024)
client.services.restart("myapp", "web")
Processes
processes = client.processes.list("myapp")
process = client.processes.get("myapp", "proc-abc123")
client.processes.stop("myapp", "proc-abc123")
Resources
# App-scoped resources
resources = client.resources.list("myapp")
resource = client.resources.get("myapp", "database")
# System-scoped resources
all_resources = client.resources.system_list()
client.resources.system_create("postgres", name="mydb")
client.resources.system_link("mydb", "myapp")
System
system = client.system.get()
print(f"{system.name} v{system.version} ({system.provider})")
capacity = client.system.capacity()
Low-Level API
Escape hatch for endpoints not covered by resource methods:
# Equivalent to `convox api get /apps`
data = client.api.get("/apps")
# POST with form data
data = client.api.post("/apps", data={"name": "newapp"})
Error Handling
All API errors raise typed exceptions:
from convox import (
ConvoxAPIError, # Base for all API errors
ConvoxAuthError, # 401 - bad credentials
ConvoxForbiddenError, # 401 - insufficient permissions
ConvoxNotFoundError, # 404 - resource not found
ConvoxValidationError, # 400 - invalid input
ConvoxConflictError, # 409 - already exists / locked
ConvoxServerError, # 500 - server error
ConvoxConnectionError, # Network failure
ConvoxTimeoutError, # Request timeout
)
try:
app = client.apps.get("myapp")
except ConvoxNotFoundError:
print("App does not exist")
except ConvoxConflictError:
print("App is locked")
except ConvoxAPIError as e:
print(f"API error {e.status_code}: {e.message}")
print(f"Request ID: {e.request_id}") # for support tickets
Retry Configuration
The SDK retries on 429 (rate limit) and 502/503/504 (gateway errors) with exponential backoff:
from convox import ConvoxClient, RetryConfig
client = ConvoxClient(
host="rack.example.com",
api_key="key",
retry=RetryConfig(
max_retries=5, # default: 3
backoff_base=2.0, # default: 1.0
backoff_max=60.0, # default: 30.0
retry_on_500=True, # default: False (500s may be permanent)
),
)
Disable retries:
client = ConvoxClient(host="...", api_key="...", retry=RetryConfig(max_retries=0))
Timeouts
The default timeout is 30 seconds. Most API operations complete well within this
limit, since build creation (builds.create) returns immediately after
dispatching the build to a Kubernetes pod.
The one exception is builds.import_build(), which is synchronous — the
server reads the entire gzip archive and pushes every service image to the
container registry before responding. This method defaults to a 1800-second
(30-minute) per-request timeout, independent of the client-wide default:
# Uses the 30-minute default automatically
build = client.builds.import_build("myapp", tarball_bytes)
# Override with a custom timeout if needed
build = client.builds.import_build("myapp", tarball_bytes, timeout=3600)
You can also pass a per-request timeout to the low-level API:
data = client.api.post("/apps/myapp/builds/import", content=tarball, timeout=600)
Streaming
The stream() method returns the raw httpx.Response without checking for
error status codes. This is intentional: streaming endpoints (logs, build
output) may send partial data before an error occurs.
response = client.stream("GET", "/apps/myapp/logs", params={"follow": "true"})
try:
if response.status_code >= 400:
print(f"Error: {response.status_code}")
else:
for line in response.iter_lines():
print(line)
finally:
response.close()
CLI Comparison
| CLI Command | SDK Equivalent |
|---|---|
convox apps |
client.apps.list() |
convox apps info -a myapp |
client.apps.get("myapp") |
convox apps create myapp |
client.apps.create("myapp") |
convox apps delete myapp |
client.apps.delete("myapp") |
convox builds -a myapp |
client.builds.list("myapp") |
convox releases -a myapp |
client.releases.list("myapp") |
convox releases promote RABCDEF -a myapp |
client.releases.promote("myapp", "RABCDEF") |
convox env -a myapp |
client.environment.get("myapp") |
convox env set KEY=val -a myapp |
client.environment.set("myapp", {"KEY": "val"}) |
convox env unset KEY -a myapp |
client.environment.unset("myapp", "KEY") |
convox api get /apps |
client.api.get("/apps") |
Contributing
# Install dev dependencies
pip install -e ".[dev]"
# Run unit tests (mocked, no rack needed)
pytest tests/
# Run integration tests (requires a live rack)
CONVOX_HOST=console.convox.com \
CONVOX_PASSWORD=your-deploy-key \
CONVOX_RACK=org/rack-name \
pytest integration_tests/ -v
# Lint and format
ruff check .
ruff format .
# Type check
mypy src/
License
Apache 2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file convox-0.1.0.tar.gz.
File metadata
- Download URL: convox-0.1.0.tar.gz
- Upload date:
- Size: 36.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f0c053a30eaadfe56adc20b847b5e8880b4f70ec334f9ce2dbafcd52012f49d8
|
|
| MD5 |
0bf9720864e5e5cf3d09e1ef4f0000bf
|
|
| BLAKE2b-256 |
a48a9a1b42cb739ee10a6867b3d1a4593505f989261d74374959fce9319a0384
|
File details
Details for the file convox-0.1.0-py3-none-any.whl.
File metadata
- Download URL: convox-0.1.0-py3-none-any.whl
- Upload date:
- Size: 29.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d31418affcb7d650318a16d6e4714cfd6e063b8cccfd447f70b6d9be1831ffcd
|
|
| MD5 |
d1615463cf1bbdc87b049cf046497d09
|
|
| BLAKE2b-256 |
15400cfdb338905bfe7975a4f5ea3f8ed10b754afe3dab91df294395f8f462c2
|