mesa-rest) provides a typed Python client for interacting with the Depot API. It follows the same OpenAPI contract as the TypeScript SDK, with resource-based accessors, pagination support, retries, and structured errors.
Installation
pip install mesa-rest
Quick Start
import os
from mesa_rest import Mesa
client = Mesa(
api_key=os.environ["MESA_API_KEY"],
)
# Create a repository
repo = client.repos.create(
org="my-org",
body={"name": "my-repo"},
)
# List repositories
repos_page = client.repos.list(org="my-org")
print(repos_page)
Configuration
You can configure authentication, server URL, timeout, and retries when constructing the client.from mesa_rest import Mesa
client = Mesa(
# Required: API key for authentication
api_key="your-api-key",
# Optional: Override base URL
server_url="https://api.mesa.dev/v1",
# Optional: Global timeout in milliseconds
timeout_ms=30_000,
# Optional: Retry configuration
retry_config={
"strategy": "backoff",
"backoff": {
"initial_interval": 500,
"max_interval": 60_000,
"exponent": 1.5,
"max_elapsed_time": 300_000,
},
},
)
Environment Variables
The SDK can read credentials from environment variables when not explicitly configured:MESA_API_KEY- your API key
Resources
The SDK organizes methods into resource namespaces that mirror the API structure.Repositories
# Create a repository
repo = client.repos.create(
org="my-org",
body={
"name": "my-repo",
"default_bookmark": "main", # optional
},
)
# List repositories (paginated)
repos_page = client.repos.list(
org="my-org",
limit=50,
cursor="...", # optional
)
# Get a specific repository
repo = client.repos.get(
org="my-org",
repo="my-repo",
)
# Update a repository
updated = client.repos.update(
org="my-org",
repo="my-repo",
body={"name": "new-name"},
)
# Delete a repository
client.repos.delete(
org="my-org",
repo="my-repo",
)
Bookmarks
# List bookmarks
bookmarks_page = client.bookmarks.list(
org="my-org",
repo="my-repo",
)
# Create a bookmark
bookmark = client.bookmarks.create(
org="my-org",
repo="my-repo",
body={
"name": "feature-bookmark",
"change_id": "xyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxy",
},
)
# Delete a bookmark
client.bookmarks.delete(
org="my-org",
repo="my-repo",
bookmark="feature-bookmark",
)
Changes
# List changes
changes_page = client.changes.list(
org="my-org",
repo="my-repo",
bookmark="main", # optional
)
# Get a change
change = client.changes.get(
org="my-org",
repo="my-repo",
change_id="xyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxy",
)
# Create a change
new_change = client.changes.create(
org="my-org",
repo="my-repo",
body={
"base_change_id": "xyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxy",
"message": "Add README",
"author": {
"name": "Bot",
"email": "bot@example.com",
},
"files": [
{
"path": "README.md",
"action": "upsert",
"content": "IyBIZWxsbyBmcm9tIE1lc2EK",
"encoding": "base64",
}
],
},
)
Content
# Get file content
content = client.content.get(
org="my-org",
repo="my-repo",
path="src/index.py",
ref="main", # optional
)
# Handle file vs directory responses
if getattr(content, "type", None) == "file":
print(content.content)
else:
print(content.entries)
Diffs
diff = client.diffs.get(
org="my-org",
repo="my-repo",
base_change_id="xyzxyzxyzxyzxyzxyzxyzxyzxyzxyzxy",
head_change_id="wvutsrqponmlkzyxwvutsrqponmlkzyx",
)
for file in diff.files:
print(f"{file.change_type}: {file.path}")
Merge
# Merge a branch into another (fast-forward when possible)
result = client.merge.perform(
org="my-org",
repo="my-repo",
base="main",
body={
"head": "feature-branch",
"author": {
"name": "Bot",
"email": "bot@example.com",
},
},
)
print(result.merge_type) # "fast_forward" or "merge_commit"
print(result.sha) # Resulting commit SHA
# Merge with a custom commit message
result = client.merge.perform(
org="my-org",
repo="my-repo",
base="main",
body={
"head": "feature-branch",
"message": "Ship feature X",
"author": {
"name": "Bot",
"email": "bot@example.com",
},
},
)
# Merge and delete the head branch afterward
result = client.merge.perform(
org="my-org",
repo="my-repo",
base="main",
body={
"head": "feature-branch",
"author": {
"name": "Bot",
"email": "bot@example.com",
},
"delete_branch": True,
},
)
print(result.branch_deleted) # True
The merge endpoint returns
409 MERGE_CONFLICT when the branches have conflicting changes that cannot be auto-resolved. Catch this error to handle conflicts in your workflow.Admin (API Keys)
# Create API key
new_key = client.admin.create_api_key(
org="my-org",
body={
"name": "CI Key",
"scopes": ["write"],
},
)
print(new_key.key)
# List API keys
keys = client.admin.list_api_keys(org="my-org")
# Revoke API key
client.admin.revoke_api_key(
org="my-org",
id="key_123",
)
Pagination
List endpoints support cursor-based pagination.cursor = None
while True:
page = client.repos.list(
org="my-org",
cursor=cursor,
limit=100,
)
for repo in page.result.repos:
print(repo.name)
cursor = page.result.next_cursor
if not cursor:
break
Error Handling
The SDK raises exceptions for request and response failures. Catch and branch on error type or status as needed by your application.try:
client.repos.get(org="my-org", repo="missing")
except Exception as exc:
# Handle SDK-specific errors in your application layer.
# Common categories: auth errors, validation errors, not found,
# conflict, and transport failures.
print(f"Request failed: {exc}")
raise
Error Shape
API errors follow a consistent shape:{
"error": {
"code": "NOT_FOUND",
"message": "Repository not found",
"details": {}
}
}
Per-Request Options
You can override client defaults for a single request (for example timeout, retries, or headers).repo = client.repos.get(
org="my-org",
repo="my-repo",
request_options={
"timeout_ms": 60_000,
"headers": {
"X-Custom-Header": "value",
},
},
)
Complete Example
import os
from mesa_rest import Mesa
def main() -> None:
client = Mesa(api_key=os.environ["MESA_API_KEY"])
# Create repository
repo = client.repos.create(
org="my-org",
body={"name": "example-repo"},
)
# Initial change on main
main_change = client.changes.create(
org="my-org",
repo="example-repo",
body={
"base_change_id": repo.head_change_id,
"message": "Initial change",
"author": {
"name": "Bot",
"email": "bot@example.com",
},
"files": [
{
"path": "README.md",
"action": "upsert",
"content": "IyBFeGFtcGxlIFJlcG9zaXRvcnkK",
"encoding": "base64",
}
],
},
)
# Create feature bookmark from the unpublished main change head
client.bookmarks.create(
org="my-org",
repo="example-repo",
body={
"name": "feature",
"change_id": main_change.id,
},
)
# Create a feature change
client.changes.create(
org="my-org",
repo="example-repo",
body={
"base_change_id": main_change.id,
"message": "Add feature",
"author": {
"name": "Bot",
"email": "bot@example.com",
},
"files": [
{
"path": "feature.py",
"action": "upsert",
"content": "RkVBVFVSRV9FTkFCTEVEID0gVHJ1ZQo=",
"encoding": "base64",
}
],
},
)
# Diff feature against main
diff = client.diffs.get(
org="my-org",
repo="example-repo",
base="main",
head="feature",
)
print(f"Changes: {len(diff.files)} files")
# Merge feature branch into main and clean up
merge = client.merge.perform(
org="my-org",
repo="example-repo",
base="main",
body={
"head": "feature",
"author": {
"name": "Bot",
"email": "bot@example.com",
},
"delete_branch": True,
},
)
print(f"Merged via {merge.merge_type}: {merge.sha}")
if __name__ == "__main__":
main()
For endpoint behavior and payload details shared across all SDKs, see the HTTP API Overview.

