clickup
ClickUp API Skill (No MCP)
Overview
Interact with ClickUp workspaces using direct REST API calls (curl). No MCP server required — just a personal API token stored in CLICKUP_TOKEN.
Setup
1. Get Your API Token
Go to ClickUp > Settings > Apps > API Token and generate a personal token.
2. Store Token
export CLICKUP_TOKEN="pk_YOUR_TOKEN_HERE"
Store in .env or export before running commands.
3. Persist Token (Recommended)
Add to ~/.zshrc or ~/.zprofile so it survives shell restarts:
export CLICKUP_TOKEN="pk_YOUR_TOKEN_HERE"
Note: Tokens set only in the terminal session won't be inherited by Claude Code's Bash subshells. Persisting in your shell profile ensures it's always available.
4. Verify Connection
curl -s --header "Authorization: $CLICKUP_TOKEN" \
https://api.clickup.com/api/v2/user | python3 -m json.tool
zsh tip: Always use
--headerinstead of-Hin Claude Code's Bash tool —-Hcan be misinterpreted as a shell flag in zsh subshells.
ClickUp Hierarchy
Workspace (Team) → Space → Folder (optional) → List → Task → Subtask
Key terminology: In API v2, "Team" = Workspace, "Project" = Folder.
Quick Reference
| Action | Method | Endpoint |
|---|---|---|
| Get user | GET | /api/v2/user |
| Get workspaces | GET | /api/v2/team |
| Get spaces | GET | /api/v2/team/{team_id}/space |
| Get folders | GET | /api/v2/space/{space_id}/folder |
| Get lists (folder) | GET | /api/v2/folder/{folder_id}/list |
| Get lists (folderless) | GET | /api/v2/space/{space_id}/list |
| Get tasks | GET | /api/v2/list/{list_id}/task |
| Get single task | GET | /api/v2/task/{task_id} |
| Create task | POST | /api/v2/list/{list_id}/task |
| Update task | PUT | /api/v2/task/{task_id} |
| Delete task | DELETE | /api/v2/task/{task_id} |
| Get comments | GET | /api/v2/task/{task_id}/comment |
| Add comment | POST | /api/v2/task/{task_id}/comment |
| Get custom fields | GET | /api/v2/list/{list_id}/field |
| Set custom field | POST | /api/v2/task/{task_id}/field/{field_id} |
| Get time entries | GET | /api/v2/team/{team_id}/time_entries |
| Create time entry | POST | /api/v2/team/{team_id}/time_entries |
| Start timer | POST | /api/v2/team/{team_id}/time_entries/start |
| Stop timer | POST | /api/v2/team/{team_id}/time_entries/stop |
| Get members (task) | GET | /api/v2/task/{task_id}/member |
| Get members (list) | GET | /api/v2/list/{list_id}/member |
| Get tags | GET | /api/v2/space/{space_id}/tag |
| Get goals | GET | /api/v2/team/{team_id}/goal |
| Create webhook | POST | /api/v2/team/{team_id}/webhook |
Base URL: https://api.clickup.com
Common Workflows
Discovery: Find Your Workspace → Space → List IDs
# Step 1: Get workspace (team) ID
curl -s --header "Authorization: $CLICKUP_TOKEN" \
https://api.clickup.com/api/v2/team | python3 -c "
import sys, json
teams = json.load(sys.stdin)['teams']
for t in teams:
print(f\"{t['id']}: {t['name']}\")"
# Step 2: Get spaces in workspace
curl -s --header "Authorization: $CLICKUP_TOKEN" \
"https://api.clickup.com/api/v2/team/{TEAM_ID}/space" | python3 -c "
import sys, json
spaces = json.load(sys.stdin)['spaces']
for s in spaces:
print(f\"{s['id']}: {s['name']}\")"
# Step 3: Get lists (folderless)
curl -s --header "Authorization: $CLICKUP_TOKEN" \
"https://api.clickup.com/api/v2/space/{SPACE_ID}/list" | python3 -c "
import sys, json
lists = json.load(sys.stdin)['lists']
for l in lists:
print(f\"{l['id']}: {l['name']}\")"
Search for a Task by Name
curl -s --header "Authorization: $CLICKUP_TOKEN" \
"https://api.clickup.com/api/v2/team/{TEAM_ID}/task?search=Task+Name&subtasks=true&include_closed=true" | python3 -c "
import sys, json
for t in json.load(sys.stdin).get('tasks', []):
print(f\"{t['id']}: [{t['status']['status']}] {t['name']}\")"
Get Available Statuses for a List
curl -s --header "Authorization: $CLICKUP_TOKEN" \
"https://api.clickup.com/api/v2/list/{LIST_ID}" | python3 -c "
import sys, json
d = json.load(sys.stdin)
for s in d.get('statuses', []):
print(f\"{s['status']} (type: {s['type']})\")
"
Create a Task
curl -s -X POST \
--header "Authorization: $CLICKUP_TOKEN" \
--header "Content-Type: application/json" \
"https://api.clickup.com/api/v2/list/{LIST_ID}/task" \
--data '{
"name": "My New Task",
"description": "Task description here",
"status": "to do",
"priority": 3,
"assignees": [],
"tags": [],
"due_date": null,
"notify_all": false
}'
Priority values: 1 = Urgent, 2 = High, 3 = Normal, 4 = Low
Bulk Create Tasks (Python — Recommended for batches)
Use Python subprocess for bulk operations to avoid zsh flag interpretation issues:
import subprocess, json
TOKEN = "pk_YOUR_TOKEN"
LIST_ID = "YOUR_LIST_ID"
tasks = [
("Task name", "to do"),
("Another task", "in progress"),
("Finished task", "done"),
]
for name, status in tasks:
payload = json.dumps({"name": name, "status": status})
result = subprocess.run(
["curl", "-s", "-X", "POST",
"--header", f"Authorization: {TOKEN}",
"--header", "Content-Type: application/json",
f"https://api.clickup.com/api/v2/list/{LIST_ID}/task",
"--data", payload],
capture_output=True, text=True
)
d = json.loads(result.stdout)
print(f"[{d['status']['status']}] {d['name']}" if "id" in d else f"ERROR: {d.get('err')}")
Create a Subtask
Same as creating a task, but add "parent": "{parent_task_id}" to the JSON body.
Update a Task
curl -s -X PUT \
--header "Authorization: $CLICKUP_TOKEN" \
--header "Content-Type: application/json" \
"https://api.clickup.com/api/v2/task/{TASK_ID}" \
--data '{
"name": "Updated Name",
"status": "in progress",
"priority": 2
}'
Get Tasks with Pagination
PAGE=0
while true; do
RESULT=$(curl -s --header "Authorization: $CLICKUP_TOKEN" \
"https://api.clickup.com/api/v2/list/{LIST_ID}/task?page=$PAGE")
COUNT=$(echo "$RESULT" | python3 -c "import sys,json; print(len(json.load(sys.stdin)['tasks']))")
echo "$RESULT" | python3 -c "
import sys, json
for t in json.load(sys.stdin)['tasks']:
print(f\"{t['id']}: [{t['status']['status']}] {t['name']}\")"
[ "$COUNT" -lt 100 ] && break
PAGE=$((PAGE + 1))
done
Add a Comment
curl -s -X POST \
--header "Authorization: $CLICKUP_TOKEN" \
--header "Content-Type: application/json" \
"https://api.clickup.com/api/v2/task/{TASK_ID}/comment" \
--data '{
"comment_text": "This is a comment from the API",
"notify_all": false
}'
Track Time
# Get time entries (date range in milliseconds)
curl -s --header "Authorization: $CLICKUP_TOKEN" \
"https://api.clickup.com/api/v2/team/{TEAM_ID}/time_entries?start_date=1700000000000&end_date=1710000000000"
# Create a time entry (duration in milliseconds)
curl -s -X POST \
--header "Authorization: $CLICKUP_TOKEN" \
--header "Content-Type: application/json" \
"https://api.clickup.com/api/v2/team/{TEAM_ID}/time_entries" \
--data '{
"tid": "{TASK_ID}",
"description": "Working on feature",
"start": 1700000000000,
"duration": 3600000
}'
Rate Limits
- 100 requests/minute per token (all plans)
- HTTP 429 returned when exceeded
- Check response headers:
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset - Use webhooks instead of polling where possible
Pagination
- Use
pagequery param (0-indexed) with max 100 results per page - Loop until returned items < 100 (indicates last page)
- Applies to: tasks, time entries, comments, and other list endpoints
Common Mistakes
| Mistake | Fix |
|---|---|
| Using "workspace" in URL | Use team — API v2 calls workspaces "teams" |
| Updating custom fields via PUT task | Use dedicated POST /task/{id}/field/{field_id} endpoint |
| Forgetting Content-Type header on POST/PUT | Always include Content-Type: application/json |
| Timestamps in seconds | ClickUp uses milliseconds (Unix epoch × 1000) |
| Not paginating task lists | Max 100 per page — always implement pagination loop |
| Polling instead of webhooks | Subscribe to webhook events for real-time updates |
Using -H flag in zsh subshells |
Use --header instead — -H is misinterpreted by zsh in some contexts |
| Token not available in Claude Code Bash | Persist token in ~/.zshrc or ~/.zprofile; session-only exports don't survive subshells |
| Trying to clear notifications via API | ClickUp API v2 has no notification endpoint — clear them manually in the UI |
Searching tasks without include_closed=true |
Closed/done tasks are excluded by default; add include_closed=true to find them |
Webhook Events
Subscribe to events: taskCreated, taskUpdated, taskDeleted, listCreated, listUpdated, listDeleted, folderCreated, folderUpdated, folderDeleted, spaceCreated, spaceUpdated, spaceDeleted, goalCreated, goalUpdated, goalDeleted.
See clickup-api-reference.md for the full endpoint reference.