Skip to main content
This page documents squarecloud-api 5.0, a rewrite of the SDK. Coming from v4? Read the v4 → v5 migration guide.

Requirements

The package has no runtime dependencies (only the standard library: http.client, json, ssl) and is fully typed (py.typed): responses are TypedDicts your editor and type checker understand.

Installation

The package installs as squarecloud-api and is imported as squarecloud. The installed version is squarecloud.__version__.

API key and scopes

Create a key at squarecloud.app/account/security. The SDK sends it as-is in the Authorization header (no Bearer prefix). A key can be limited to scopes (apps:read, apps:deploy, apps:control, ai:chat, …) and to specific apps or databases:
  • A call outside those limits raises a SquareCloudAPIError with 403 MISSING_SCOPE or RESOURCE_NOT_ALLOWED.
  • List methods (account.me(), apps.status_all(), …) only return the resources the key can see.
  • An unknown, revoked or expired key is 401 ACCESS_DENIED.
Keep the key out of your source code: read it from the environment (os.environ["SQUARECLOUD_API_KEY"]) or a secret manager.
The examples read the key from the SQUARECLOUD_API_KEY environment variable. Set it in the terminal where you run them:

Creating the client

The SDK has two clients with the same groups and methods:
  • SquareCloud is synchronous and thread-safe: share one instance between threads. Each thread reuses its own keep-alive connection.
  • AsyncSquareCloud is the same API with await. Each call runs the synchronous client in asyncio.to_thread, so the event loop is never blocked.
Both print your account name and the number of apps the key can see:
Leaving the with / async with block closes the pooled connections. Without a block, call client.close() when you are done. close() is a regular (synchronous) method on both clients. An empty or whitespace-only key raises a ValueError in the constructor, before any request.

The async client

AsyncSquareCloud differs from SquareCloud in only three places:
  • Every method returns a coroutine: await client.apps.status(app_id).
  • close() is synchronous: call it without await.
  • apps.realtime(app_id) is not awaited: it returns an AsyncRealtime that you consume with async for (see Realtime).
Since each call runs in a worker thread, you can run calls concurrently with asyncio.gather:
Cancelling an awaiting task does not stop the request already running in its worker thread: the call still completes (or times out) in the background.

Options

The options are keyword-only, and AsyncSquareCloud takes the same ones.
The client keeps the key only in its private request headers: there is no client.api_key attribute, and the SDK’s logger never writes it.

Modules

The package exports SquareCloud, AsyncSquareCloud, SquareCloudAPIError, BASE_URL, HTTPTransport, the Transport and Response protocols, Realtime, AsyncRealtime and __version__. The response types (App, RuntimeStats, Snapshot, …) live in squarecloud.types:

Conventions

Ids first, plain data back

Every method takes the resource id as its first argument and returns plain data: each response is a TypedDict, which is a regular dict at runtime (no classes, no cache), so fields the API adds later are kept. Field names are the API’s own (created_at, version_id, lastModified, joinedAt, netIO, …), so the API reference applies as-is.
  • Mutations return None, unless the API returns data (envs.*, deploys.set_webhook, deploys.link_github_app, databases.reset_credentials and the create methods).
  • A string result is never None: it is '' when the API sends none.
  • Lists come complete in one call: there is no pagination.
  • Optional modifiers are keyword-only (status(app_id, raw=True), commit(app_id, file, path="/src")). The one exception is the optional path of files.list, which can also be passed by position.
Methods are regular bound methods, so you can keep a reference to them:

Workspace apps

Every app_id also accepts the composite form <appId>-<workspaceId> to act on an app shared with you through a workspace. workspaces.get() and workspaces.list() return raw ids; you build the composite id yourself:

Ids are encoded

Ids in the URL path are percent-encoded. An id that is empty, . or .. would reach a different route, so it fails locally with INVALID_ID (status 0) before anything is sent. Workspace routes send their ids in the body instead: there, INVALID_ID (400) comes from the server.

Dates

start and end arguments (see Network) take an ISO 8601 string (sent as is) or a datetime (sent in UTC). A naive datetime is treated as local time and converted to UTC, so prefer aware ones (datetime.now(UTC)). Dates in responses stay as the API sends them (ISO strings, or Unix milliseconds where the API uses them).

Timeouts

timeout bounds each socket operation: the connect and every read or write. A response that keeps sending data can take longer than timeout in total. A timeout of 0 or less disables every timeout, the 120 s floors included. Calls cannot be cancelled once sent: the only stream you can stop midway is apps.realtime(), with close() from any thread. For a long upload, keep the default timeout so a dead connection is detected on connect, and run it in a thread or with AsyncSquareCloud if the rest of your program must keep going.

Account

client.account.me() returns the authenticated user plus the apps and databases the key can see.
client.account.snapshots(scope=...) lists every snapshot of the account: see Snapshots.

Platform status

client.service.status() returns the public platform status. The route needs no valid key, but the client still requires a non-empty one.
unknown means the check itself could not run: it is not evidence of an outage.

Advanced

Custom transport

transport= replaces the HTTP layer. Use it for proxies, tracing or tests. A transport is any callable with this signature:
The returned object needs status, read(), readline() and close(): an http.client.HTTPResponse qualifies. Retries, error mapping and the {status, response} unwrapping stay in the client, so a transport only moves bytes. HTTPTransport(timeout=30.0) is the default transport: one keep-alive connection per thread and host, gzip-compressed responses (except for streams), and timeout as the connect bound of the calls without their own timeout. You can wrap it:
client.close() closes the connections of the default transport. A custom transport that has a close() method is closed too.

Logging

The SDK logs each request at DEBUG on the squarecloud logger: method, path and status, never bodies or keys. It only attaches a NullHandler, so nothing is printed until you configure logging:

Next steps

Managing applications

Status, lifecycle, logs and metrics.

Errors

Error class, retries and rate limits.

API introduction

Base URL, authentication and a first request.

CLI quickstart

Deploy and manage apps from the terminal.