> ## Documentation Index
> Fetch the complete documentation index at: https://docs.noclick.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Python SDK

> Install and use the NoClick Python SDK to connect applications to workflows

# Python SDK

The Python SDK connects Python applications to NoClick workflows via WebSocket (Socket.IO). Use it for data pipelines, automation scripts, backend services, and CLI tools.

## Installation

```bash theme={null}
pip install noclick
```

Or install from source:

```bash theme={null}
cd sdk/python
pip install -e .
```

## Quick Start

```python theme={null}
import asyncio
import noclick

async def main():
    sdk = noclick.Client(
        api_key="nk_live_...",
        workflow_id="...",
    )
    await sdk.connect()

    # Read a node's output
    output = await sdk.nodes.get_output("gmail-node-id")
    for email in output.get("emails", []):
        print(f"{email['subject']} — {email['from']}")

    # Run a node and get results
    results = await sdk.execution.run_nodes_and_get_output(
        ["data-fetcher"],
        ["data-fetcher"],
        timeout=30,
    )

    await sdk.disconnect()

asyncio.run(main())
```

## Authentication

API keys are created in **Settings > Developer**. Keys have three permission levels:

| Permission | Allows                                                                                                                                         |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `read`     | `nodes.get_output`, `nodes.list`, `nodes.get_config`, `state.get`, `state.keys`, `dataset.get_rows`, `resources.list`, `auth.list_credentials` |
| `execute`  | `execution.run_nodes_and_get_output`, `execution.run_nodes_in_background`                                                                      |
| `write`    | `nodes.set_config`, `state.set`, `state.delete`, `dataset.append_rows`, `dataset.create`, `resources.upload`, `auth.create_credential`         |

## Reading Data

```python theme={null}
# List all nodes
nodes = await sdk.nodes.list()
for node in nodes:
    print(f"{node['id']} ({node['type']}) — {node['label']}")

# Read a node's last output
output = await sdk.nodes.get_output("gmail-node-id")
for email in output.get("emails", []):
    print(f"{email['subject']} — {email['from']}")

# Read a node's config
config = await sdk.nodes.get_config("http-node-id")
print(config["url"])
```

## Running Nodes

```python theme={null}
# Fire and forget
await sdk.execution.run_nodes_in_background(["data-fetcher"])

# Run and wait for specific output
results = await sdk.execution.run_nodes_and_get_output(
    ["data-fetcher"],       # nodes to execute
    ["formatter", "chart"], # nodes whose output we want
    timeout=30,
)
print(results["formatter"])
print(results["chart"])
```

## State Management

Requires a **State Manager** node in the workflow.

```python theme={null}
await sdk.state.set("counter", 0)
val = await sdk.state.get("counter")  # 0

await sdk.state.update("counter", lambda n: (n or 0) + 1)
await sdk.state.delete("counter")

keys = await sdk.state.keys()  # ["counter", ...]
```

## Credentials

```python theme={null}
# List credentials
creds = await sdk.auth.list_credentials()
for c in creds:
    print(f"{c['name']} ({c['type']})")

# Check if a credential exists
has_gmail = await sdk.auth.has_credential("google_gmail_oauth")

# Create an API key credential
cred = await sdk.auth.create_credential(
    "telegram_bot_token",
    {"token": "..."},
    name="My Bot"
)
```

## Resources (File Storage)

```python theme={null}
import aiohttp

# Upload a file
result = await sdk.resources.upload("report.pdf", "application/pdf", file_size)
async with aiohttp.ClientSession() as session:
    with open("report.pdf", "rb") as f:
        await session.put(result["upload_url"], data=f)

# Get download URL
url = await sdk.resources.get_url(result["resource_id"])

# List resources
files = await sdk.resources.list("file")
```

## Dataset CRUD

```python theme={null}
# Create a dataset
ds_id = await sdk.dataset.create("User Submissions")

# Append rows
count = await sdk.dataset.append_rows(ds_id, [
    {"name": "Alice", "score": 95},
    {"name": "Bob", "score": 87},
])

# Read rows (paginated)
page = await sdk.dataset.get_rows(ds_id, limit=100)
for row in page["rows"]:
    print(f"{row['id']}: {row['data']}")

# Update a row
await sdk.dataset.update_row(ds_id, row_id, {"score": 98})

# Delete rows
await sdk.dataset.delete_rows(ds_id, [row_id])

# List datasets
datasets = await sdk.dataset.list()
```

## Real-time Subscriptions

```python theme={null}
# Subscribe to node output changes
def on_output(node_id, output):
    print(f"New output from {node_id}")

sdk.execution.on_node_output("data-fetcher", on_output)
```

## Full API Reference

See the [API Reference](/sdk/api-reference) for complete method signatures with TypeScript and Python examples side by side.
