Python client for the Orca Agent Engine API. Documentation lives at runorca.ai.
Install the SDK from PyPI:
pip install runorcaThe distribution is named runorca, but the package you import is still orca.
Python 3.10 or later is required.
To install unreleased changes straight from this repository:
pip install "runorca @ git+https://github.com/orca-ae/orca-sdk-python"Append @<tag or commit> to the URL to pin a version. If you previously installed
this SDK from Git as orca-sdk, uninstall that distribution before installing
runorca; both distributions install the same orca import package.
Note: do not run
pip install orca-sdk— that name belongs to an unrelated package on public PyPI.
import os
from orca import Orca
client = Orca(
api_key=os.environ.get("ORCA_API_KEY"),
base_url=os.environ.get("ORCA_BASE_URL"),
)
agent = client.agents.create(model="some-model", name="My First Agent")
print(agent.id)Every method is available on an async client with the same signature:
import asyncio
from orca import AsyncOrca
client = AsyncOrca()
async def main() -> None:
agent = await client.agents.create(model="some-model", name="My First Agent")
print(agent.id)
asyncio.run(main())| Option | Environment variable | Default |
|---|---|---|
api_key |
ORCA_API_KEY |
— |
base_url |
ORCA_BASE_URL |
required |
timeout |
— | 600 seconds |
max_retries |
— | 2 |
base_url is the host root. The SDK writes the /v1/... and /apis/... prefixes
itself, so pass https://orca.example, not https://orca.example/v1. A trailing /v1,
/v1/registry, or /api/v1 is stripped with a deprecation warning.
There is no default host: this API is self-hosted, so a missing base URL raises rather than silently pointing somewhere unexpected.
# A literal token
client = Orca(api_key="sk-...")
# Resolved per request -- the hook for short-lived or rotating tokens
client = Orca(api_key=lambda: read_current_token())
# No Authorization header, for a deployment behind an authenticating proxy
client = Orca(api_key=None)The async client also accepts a coroutine function.
List methods return a page that iterates across page boundaries automatically:
for agent in client.agents.list():
print(agent.id)async for agent in client.agents.list():
print(agent.id)To handle pages yourself:
page = client.agents.list(limit=20)
print(page.data, page.next_page)Session events arrive as server-sent events:
session = client.sessions.create(agent="agent_id", environment_id="env_id")
client.sessions.events.send(
session.id,
events=[{"type": "user.message", "content": [{"type": "text", "text": "Hello"}]}],
)
for event in client.sessions.events.stream(session.id):
if event.type == "session.status_idle":
breakEvent names are not constrained by the SDK: every well-formed frame is yielded and you
discriminate on the payload's own type.
client.session(id) returns a handle that carries the session id for you:
handle = client.session("session_123")
handle.events.send(events=[{"type": "user.message", "content": [...]}])
for thread in handle.threads.list():
print(thread.id)
response = handle.files.download("file_123")metadata = client.files.upload(file=("hello.txt", b"hello\n", "text/plain"))Anything accepted by FileTypes works: bytes, a file object, a path, or a
(filename, content, content_type) tuple.
from orca import Orca, OrcaError, APIError, NotFoundError, RateLimitError
try:
client.agents.retrieve("missing_id")
except NotFoundError as err:
print("not found:", err.status_code)
except RateLimitError as err:
print("rate limited; retry-after:", err.headers.get("retry-after"))
except APIError as err:
print(err.status_code, err.message)
except OrcaError as err:
print("client-side error:", err)| Class | Status |
|---|---|
BadRequestError |
400 |
AuthenticationError |
401 |
PermissionDeniedError |
403 |
NotFoundError |
404 |
ConflictError |
409 |
UnprocessableEntityError |
422 |
RateLimitError |
429 |
InternalServerError |
5xx |
APIConnectionError |
network |
APIConnectionTimeoutError |
timeout |
ExtensionNotAvailableError |
client-side gate |
Guardrail management and effective model pricing are exposed as top-level resources.
Like cloud methods, they check extension discovery first and raise
ExtensionNotAvailableError before issuing the business request when unavailable:
guardrail = client.guardrails.create(
name="Protect production",
phases=["tool_call"],
scope="explicit",
rule={"kind": "builtin", "builtin": "block_tools", "params": {"tools": ["shell"]}},
)
agent = client.agents.create(
model="some-model",
name="Guarded agent",
guardrail_ids=[guardrail.id],
extra_headers={"orca-beta": "managed-agents-2026-04-01"},
)
for price in client.model_prices.list():
print(price.provider, price.model_id, price.input_per_million_tokens)guardrail_ids is optional on agent create/update and session-local agent
overrides. It is not supported by deployment APIs. The SDK probes the policy
extension only when the field is explicitly supplied.
Methods under client.cloud.* are served by the hosted extension group, which only the
hosted service serves; a self-hosted engine does not. On a deployment that does not serve
it, they raise before making any request:
from orca import ExtensionNotAvailableError
try:
providers = client.cloud.agents.providers.list()
except ExtensionNotAvailableError as err:
print(f"this deployment has no {err.group!r} extension installed")To check first:
groups = client.discovery.groups()
if any(g.name == "cloud.sn.io" for g in groups.groups):
...Failed requests are retried twice by default, with exponential backoff honouring
retry-after. Connection errors, timeouts, 408, 409, 429, and 5xx are retried.
client = Orca(max_retries=3, timeout=30.0)
client.agents.list(timeout=5.0) # per requestresponse = client.agents.with_raw_response.list()
print(response.headers.get("request-id"))
agents = response.parse()with_streaming_response defers reading the body:
with client.agents.with_streaming_response.list() as response:
print(response.headers)
agents = response.parse()This package follows semantic versioning. Internal names prefixed with an underscore are not part of the public surface.
Contributions are welcome. CONTRIBUTING.md covers the workflow, including the DCO sign-off every commit needs, and AGENTS.md holds the conventions every change follows. To get a working checkout:
./scripts/bootstrap
./scripts/test
./scripts/lintAsk questions and share ideas in GitHub Discussions. Report SDK bugs in this repository's issues, and server behavior in the engine's issues.
Please don't report security vulnerabilities in public issues. Use GitHub's private vulnerability reporting, or email [email protected].
The SDK's own code is licensed under the Apache License 2.0. It also
includes code under MIT, BSD-3-Clause, and MPL-2.0: NOTICE identifies the
covered code, and THIRD_PARTY_NOTICES contains the license
texts. The MPL-2.0 terms apply to src/orca/_utils/_utils.py, which contains copied
MPL code. Both the wheel and source distribution include these notices and that
source file.