Skip to main content

Installation

pip install nen-sdk-python
Or with uv:
uv add nen-sdk-python
Requires Python 3.13+.

Quick Start

from nen_sdk import NenDesktop

client = NenDesktop(api_key="sk_nen_...")

# Create a desktop
desktop = client.create_desktop()
did = desktop.desktop_id

# Take a screenshot
result = client.screenshot(did)
# result.base64_image is a PNG, base64-encoded

# Click and type
client.left_click(did, 500, 400)
client.type_text(did, "hello world")

# Clean up
client.delete_desktop(did)

Configuration

client = NenDesktop(
    api_key="sk_nen_...",
    base_url="https://desktop.api.getnen.ai",  # default
    timeout=30.0,                               # default (seconds)
)
The execute() method always uses a 120-second timeout, regardless of the client timeout.

Context Manager

The client supports with to automatically close the underlying HTTP connection:
with NenDesktop(api_key="sk_nen_...") as client:
    desktop = client.create_desktop()
    # ...
    client.delete_desktop(desktop.desktop_id)

Method Reference

Desktop CRUD

MethodReturnsDescription
create_desktop(desktop_type="sandbox")DesktopCreate a new desktop
list_desktops()list[Desktop]List all active desktops
get_desktop(desktop_id)DesktopGet a single desktop by ID
update_desktop(desktop_id, *, name)DesktopRename a desktop
delete_desktop(desktop_id)DeleteResponseDelete a desktop

Computer-Use Helpers

Typed shortcuts — no need to know the wire format:
MethodDescription
screenshot(desktop_id)Take a screenshot
left_click(desktop_id, x, y)Left click at coordinate
right_click(desktop_id, x, y)Right click at coordinate
double_click(desktop_id, x, y)Double click at coordinate
middle_click(desktop_id, x, y)Middle click at coordinate
mouse_move(desktop_id, x, y)Move cursor to coordinate
type_text(desktop_id, text)Type text
key_press(desktop_id, key)Press a key or combo (e.g. "ctrl+a")
scroll(desktop_id, x, y, *, direction, amount=3)Scroll at coordinate
cursor_position(desktop_id)Get current cursor position

Tool Execution

MethodReturnsDescription
execute(desktop_id, *, tool, action, params=None)ExecuteResultExecute any tool action (raw)
list_tools(desktop_id)list[ToolSchema]List available tools as JSON Schema
get_tool_logs(desktop_id)list[dict]Get tool execution logs

Sessions

MethodReturnsDescription
create_session(desktop_id)SessionInfoCreate or reconnect an RDP session
get_session(desktop_id)SessionInfoGet current session status
delete_session(desktop_id)NoneDisconnect the session

Files

Per-desktop shared drive (EFS-backed; persists across controller rebinds; 100 MiB upload cap). All three methods accept an optional path="<subdir>" keyword argument to operate within a subdirectory; upload_file creates missing intermediate directories on the server.
MethodReturnsDescription
list_files(desktop_id, *, path=None)list[File]List files on the desktop’s drive. Entries have is_dir=True for directories.
upload_file(desktop_id, name, body, *, content_type="application/octet-stream", path=None)UploadFileResponseUpload bytes or any binary file-like
download_file(desktop_id, name, *, path=None)bytesDownload a file as raw bytes
# Upload, list, then download.
with open("report.pdf", "rb") as f:
    up = client.upload_file(did, "report.pdf", f, content_type="application/pdf")
    print(up.size, "bytes uploaded as", up.filename)

for entry in client.list_files(did):
    suffix = "/" if entry.is_dir else ""
    print(entry.name + suffix, entry.size)

# Round-trip within a subdirectory.
with open("report.pdf", "rb") as f:
    client.upload_file(did, "report.pdf", f, content_type="application/pdf", path="Documents")
for entry in client.list_files(did, path="Documents"):
    print(entry.name)
raw = client.download_file(did, "report.pdf", path="Documents")

Error Handling

All API errors raise a subclass of NenDesktopError, which carries status_code and response_body:
from nen_sdk import NenDesktop, NenDesktopError, NotFoundError, AuthenticationError

client = NenDesktop(api_key="sk_nen_...")

try:
    client.get_desktop("dsk_nonexistent")
except NotFoundError as e:
    print(f"Not found ({e.status_code})")
except AuthenticationError as e:
    print(f"Auth failed ({e.status_code})")
except NenDesktopError as e:
    print(f"API error {e.status_code}: {e.response_body}")
ExceptionStatus
AuthenticationError401
NotFoundError404
ConflictError409
ServerError5xx
NenDesktopErrorany other ≥ 400