ClickUp API Integration
Integrates with the ClickUp API v2 to programmatically manage tasks, lists, spaces, folders, goals, time tracking entries, dashboards, tags, teams, and custom fields using clickup-python-sdk.
TL;DR for Code Generation
- Use
ClickupClient.init(user_token=...) from clickup_python_sdk for REST-based access
- ClickUp API is hierarchical: Teams → Spaces → Folders → Lists → Tasks
- Always specify
include_closed and subtasks parameters for accurate task listing
- Use custom field APIs to read/write typed task metadata beyond standard fields
- Time tracking requires both start/stop time and duration — use ISO 8601 durations
- Handle
clickup_python_sdk.exceptions.ClickUpException for API-level errors
- The API uses standard REST pagination with
page and limit query params
When to Use
Use this skill when:
- Creating, updating, assigning, closing, or deleting ClickUp tasks
- Managing list structure: custom fields, tags, statuses, priorities, and assignees
- Organizing spaces and folders for team/project hierarchy
- Tracking time entries against tasks with start, stop, and duration
- Creating dashboards and views for reporting
- Automating recurring task creation from templates
- Syncing ClickUp tasks with external calendars, CRMs, or databases
When NOT to Use
- Real-time collaborative editing (ClickUp is a task/project manager, not a real-time doc editor)
- Large-scale data export (use ClickUp's CSV/Excel export or dedicated ETL integration)
- Anonymous or unauthenticated access (every request requires a valid API token)
- Replacing ClickUp Automations (use ClickUp's built-in automation rules where possible)
Core Workflow
Generate an API Token — Go to https://app.clickup.com/settings/apps and create a Personal API Token. Copy the pk_xxxxxxxx token. Checkpoint: Verify the token with client.get_teams().
Initialize the Client — client = ClickupClient.init(user_token=os.environ["CLICKUP_TOKEN"]). The SDK provides both sync and async client access. Checkpoint: Call client.get_teams() and print team names.
Navigate Hierarchy — From team → spaces → folders → lists → tasks. Use client.get_spaces(team_id=...), client.get_folders(space_id=...), client.get_lists(folder_id=...), client.get_tasks(list_id=...). Checkpoint: Confirm each level returns the expected resources.
Create or Update Tasks — Use client.create_task(list_id=..., name=..., ...) for new tasks. Use client.update_task(task_id=..., ...) for partial updates. Custom fields require client.set_custom_field(). Checkpoint: Re-fetch the task to confirm the mutation persisted.
Track Time — Use client.start_timer(task_id=...) and client.stop_timer(task_id=...) for time tracking. Create manual time entries with client.create_time_entry(). Checkpoint: Verify the time entry appears in the ClickUp task.
Handle Errors — Wrap API calls in try/except ClickUpException. Check the status_code for 401 (auth), 403 (permissions), 404 (not found), 429 (rate limit). Checkpoint: Log response.json() when available for API-side errors.
Implementation Patterns
Pattern 1: List Tasks with Custom Fields
import os
from clickup_python_sdk.api import ClickupClient
client = ClickupClient.init(user_token=os.environ["CLICKUP_TOKEN"])
def list_open_tasks(list_id: str) -> list[dict]:
"""Fetch all open tasks from a ClickUp list."""
tasks = client.get_tasks(
list_id=list_id,
include_closed=False,
subtasks=True,
order_by="due_date",
)
return tasks if tasks else []
tasks = list_open_tasks("123456789")
for task in tasks:
print(f"Task: {task.name} | Due: {task.due_date} | Assignee: {task.assignees}")
Pattern 2: Create a Task with Custom Fields
def create_tracked_task(
list_id: str,
name: str,
description: str,
priority: int = 3,
assignees: list[int] | None = None,
due_date: int | None = None,
) -> dict:
"""Create a ClickUp task with priority, assignees, and due date."""
params = {
"name": name,
"description": description,
"priority": priority, # 1=urgent, 2=high, 3=normal, 4=low
"assignees": assignees or [],
}
if due_date:
params["due_date"] = due_date # Unix timestamp in milliseconds
task = client.create_task(list_id=list_id, **params)
return task
# Create a high-priority task due tomorrow (Unix ms)
import time
due = int((time.time() + 86400) * 1000)
task = create_tracked_task(
list_id="123456789",
name="Fix login timeout bug",
description="Users report 502 errors on login after 60s idle.",
priority=2,
assignees=[12345],
due_date=due,
)
print(f"Created task: {task.id} — {task.url}")
Pattern 3: Time Tracking
from datetime import datetime, timezone
def log_time_entry(
task_id: str,
duration_minutes: int,
description: str,
billable: bool = True,
) -> dict:
"""Log a manual time entry against a ClickUp task."""
start_ms = int(datetime.now(timezone.utc).timestamp() * 1000)
duration_ms = duration_minutes * 60 * 1000
try:
entry = client.create_time_entry(
task_id=task_id,
start=start_ms,
duration=duration_ms,
description=description,
billable=billable,
)
return entry
except Exception as e:
print(f"Failed to log time: {e}")
raise
log_time_entry("abc123_task", 45, "Code review and merge", billable=True)
Pattern 4: BAD vs GOOD — Task Updates
# ❌ BAD — fetches entire task object, modifies, re-posts
task = client.get_task(task_id="abc123")
task.name = "Updated Name"
task.description = "Updated desc"
client.update_task(task_id="abc123", name=task.name, description=task.description)
# ✅ GOOD — partial update with only changed fields
client.update_task(
task_id="abc123",
name="Updated Name",
description="Updated desc",
)
Pattern 5: BAD vs GOOD — Error Handling
# ❌ BAD — bare except, loses API error details
try:
client.get_task(task_id="nonexistent")
except Exception as e:
print("Error:", e)
# ✅ GOOD — typed ClickUp exception
from clickup_python_sdk.exceptions import ClickUpException
def safe_get_task(task_id: str) -> dict | None:
"""Fetch a task with resilient error handling."""
try:
return client.get_task(task_id=task_id)
except ClickUpException as e:
status = getattr(e, "status_code", 0)
if status == 404:
print(f"Task {task_id} not found.")
return None
if status == 429:
print("Rate limited — retry later.")
return None
print(f"ClickUp API error (status {status}): {e}")
return None
Constraints
MUST DO
- Use environment variables for the API token — never hardcode it
- Always set
include_closed=False unless you specifically need archived tasks
- Use Unix timestamps in milliseconds for all date/time parameters
- Paginate task lists with
page and limit params (max 100 per page)
- Verify custom field IDs and types before writing values
MUST NOT DO
- Assume task IDs are human-readable — they are opaque strings
- Poll tasks for real-time updates (use ClickUp webhooks instead)
- Create duplicate tagging structures — check existing tags first
- Use personal tokens in client-side or public applications
Output Template
Every integration function should expose:
- Client Initialization —
ClickupClient.init(user_token=...) with token from env
- Hierarchy Navigation — Team → Space → Folder → List → Task resolution
- Mutation — Task creation/update with typed parameters
- Time Tracking — Start/stop or manual time entry with ISO 8601 duration
- Error Handling —
try/except ClickUpException with status-specific recovery
Related Skills
| Skill | Purpose |
|
1---2name: clickup-api3description: Integrates with ClickUp API v2 to manage tasks, lists, spaces, folders, goals, time tracking, dashboards, and teams using clickup-python-sdk.4license: MIT5---67891011# ClickUp API Integration1213Integrates with the ClickUp API v2 to programmatically manage tasks, lists, spaces, folders, goals, time tracking entries, dashboards, tags, teams, and custom fields using `clickup-python-sdk`.1415## TL;DR for Code Generation1617- Use `ClickupClient.init(user_token=...)` from `clickup_python_sdk` for REST-based access18- ClickUp API is hierarchical: Teams → Spaces → Folders → Lists → Tasks19- Always specify `include_closed` and `subtasks` parameters for accurate task listing20- Use custom field APIs to read/write typed task metadata beyond standard fields21- Time tracking requires both start/stop time and duration — use ISO 8601 durations22- Handle `clickup_python_sdk.exceptions.ClickUpException` for API-level errors23- The API uses standard REST pagination with `page` and `limit` query params2425## When to Use2627Use this skill when:2829- Creating, updating, assigning, closing, or deleting ClickUp tasks30- Managing list structure: custom fields, tags, statuses, priorities, and assignees31- Organizing spaces and folders for team/project hierarchy32- Tracking time entries against tasks with start, stop, and duration33- Creating dashboards and views for reporting34- Automating recurring task creation from templates35- Syncing ClickUp tasks with external calendars, CRMs, or databases3637## When NOT to Use3839- Real-time collaborative editing (ClickUp is a task/project manager, not a real-time doc editor)40- Large-scale data export (use ClickUp's CSV/Excel export or dedicated ETL integration)41- Anonymous or unauthenticated access (every request requires a valid API token)42- Replacing ClickUp Automations (use ClickUp's built-in automation rules where possible)4344## Core Workflow45461. **Generate an API Token** — Go to `https://app.clickup.com/settings/apps` and create a Personal API Token. Copy the `pk_xxxxxxxx` token. **Checkpoint:** Verify the token with `client.get_teams()`.47482. **Initialize the Client** — `client = ClickupClient.init(user_token=os.environ["CLICKUP_TOKEN"])`. The SDK provides both sync and async client access. **Checkpoint:** Call `client.get_teams()` and print team names.49503. **Navigate Hierarchy** — From team → spaces → folders → lists → tasks. Use `client.get_spaces(team_id=...)`, `client.get_folders(space_id=...)`, `client.get_lists(folder_id=...)`, `client.get_tasks(list_id=...)`. **Checkpoint:** Confirm each level returns the expected resources.51524. **Create or Update Tasks** — Use `client.create_task(list_id=..., name=..., ...)` for new tasks. Use `client.update_task(task_id=..., ...)` for partial updates. Custom fields require `client.set_custom_field()`. **Checkpoint:** Re-fetch the task to confirm the mutation persisted.53545. **Track Time** — Use `client.start_timer(task_id=...)` and `client.stop_timer(task_id=...)` for time tracking. Create manual time entries with `client.create_time_entry()`. **Checkpoint:** Verify the time entry appears in the ClickUp task.55566. **Handle Errors** — Wrap API calls in `try/except ClickUpException`. Check the `status_code` for 401 (auth), 403 (permissions), 404 (not found), 429 (rate limit). **Checkpoint:** Log `response.json()` when available for API-side errors.5758## Implementation Patterns5960### Pattern 1: List Tasks with Custom Fields6162```python63import os64from clickup_python_sdk.api import ClickupClient6566client = ClickupClient.init(user_token=os.environ["CLICKUP_TOKEN"])6768def list_open_tasks(list_id: str) -> list[dict]:69 """Fetch all open tasks from a ClickUp list."""70 tasks = client.get_tasks(71 list_id=list_id,72 include_closed=False,73 subtasks=True,74 order_by="due_date",75 )76 return tasks if tasks else []7778tasks = list_open_tasks("123456789")79for task in tasks:80 print(f"Task: {task.name} | Due: {task.due_date} | Assignee: {task.assignees}")81```8283### Pattern 2: Create a Task with Custom Fields8485```python86def create_tracked_task(87 list_id: str,88 name: str,89 description: str,90 priority: int = 3,91 assignees: list[int] | None = None,92 due_date: int | None = None,93) -> dict:94 """Create a ClickUp task with priority, assignees, and due date."""95 params = {96 "name": name,97 "description": description,98 "priority": priority, # 1=urgent, 2=high, 3=normal, 4=low99 "assignees": assignees or [],100 }101 if due_date:102 params["due_date"] = due_date # Unix timestamp in milliseconds103 task = client.create_task(list_id=list_id, **params)104 return task105106# Create a high-priority task due tomorrow (Unix ms)107import time108due = int((time.time() + 86400) * 1000)109task = create_tracked_task(110 list_id="123456789",111 name="Fix login timeout bug",112 description="Users report 502 errors on login after 60s idle.",113 priority=2,114 assignees=[12345],115 due_date=due,116)117print(f"Created task: {task.id} — {task.url}")118```119120### Pattern 3: Time Tracking121122```python123from datetime import datetime, timezone124125def log_time_entry(126 task_id: str,127 duration_minutes: int,128 description: str,129 billable: bool = True,130) -> dict:131 """Log a manual time entry against a ClickUp task."""132 start_ms = int(datetime.now(timezone.utc).timestamp() * 1000)133 duration_ms = duration_minutes * 60 * 1000134135 try:136 entry = client.create_time_entry(137 task_id=task_id,138 start=start_ms,139 duration=duration_ms,140 description=description,141 billable=billable,142 )143 return entry144 except Exception as e:145 print(f"Failed to log time: {e}")146 raise147148log_time_entry("abc123_task", 45, "Code review and merge", billable=True)149```150151### Pattern 4: BAD vs GOOD — Task Updates152153```python154# ❌ BAD — fetches entire task object, modifies, re-posts155task = client.get_task(task_id="abc123")156task.name = "Updated Name"157task.description = "Updated desc"158client.update_task(task_id="abc123", name=task.name, description=task.description)159160# ✅ GOOD — partial update with only changed fields161client.update_task(162 task_id="abc123",163 name="Updated Name",164 description="Updated desc",165)166```167168### Pattern 5: BAD vs GOOD — Error Handling169170```python171# ❌ BAD — bare except, loses API error details172try:173 client.get_task(task_id="nonexistent")174except Exception as e:175 print("Error:", e)176177# ✅ GOOD — typed ClickUp exception178from clickup_python_sdk.exceptions import ClickUpException179180def safe_get_task(task_id: str) -> dict | None:181 """Fetch a task with resilient error handling."""182 try:183 return client.get_task(task_id=task_id)184 except ClickUpException as e:185 status = getattr(e, "status_code", 0)186 if status == 404:187 print(f"Task {task_id} not found.")188 return None189 if status == 429:190 print("Rate limited — retry later.")191 return None192 print(f"ClickUp API error (status {status}): {e}")193 return None194```195196## Constraints197198### MUST DO199- Use environment variables for the API token — never hardcode it200- Always set `include_closed=False` unless you specifically need archived tasks201- Use Unix timestamps in milliseconds for all date/time parameters202- Paginate task lists with `page` and `limit` params (max 100 per page)203- Verify custom field IDs and types before writing values204205### MUST NOT DO206- Assume task IDs are human-readable — they are opaque strings207- Poll tasks for real-time updates (use ClickUp webhooks instead)208- Create duplicate tagging structures — check existing tags first209- Use personal tokens in client-side or public applications210211## Output Template212213Every integration function should expose:2142151. **Client Initialization** — `ClickupClient.init(user_token=...)` with token from env2162. **Hierarchy Navigation** — Team → Space → Folder → List → Task resolution2173. **Mutation** — Task creation/update with typed parameters2184. **Time Tracking** — Start/stop or manual time entry with ISO 8601 duration2195. **Error Handling** — `try/except ClickUpException` with status-specific recovery220221## Related Skills222223| Skill | Purpose |224|