Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Optimize ClickUp API v2 performance with caching, pagination, connection pooling,
and request batching patterns.
Trigger: "clickup performance", "optimize clickup", "clickup latency",
"clickup caching", "clickup slow", "clickup batch requests", "clickup pagination".
allowed-tools
Read, Write, Edit
version
1.6.0
license
MIT
author
Jeremy Longshore <jeremy@intentsolutions.io>
tags
["saas","productivity","clickup"]
compatibility
Designed for Claude Code
ClickUp Performance Tuning
Overview
Optimize ClickUp API v2 throughput and latency. Key strategies: cache hierarchy data, paginate efficiently, pool connections, and batch where possible.
Baseline Latency (ClickUp API v2)
Endpoint
Typical P50
Typical P95
GET /user
80ms
200ms
GET /team
100ms
300ms
GET /list/{id}/task
150ms
500ms
POST /list/{id}/task
200ms
600ms
PUT /task/{id}
150ms
400ms
GET /task/{id} (with custom fields)
200ms
700ms
1. Cache Hierarchy Data
Workspaces, spaces, folders, and lists change infrequently. Cache them.
import { LRUCache } from'lru-cache';
const clickupCache = newLRUCache<string, any>({
max: 1000,
ttl: 300_000, // 5 min for structural data
});
asyncfunction cachedRequest<T>(path: string, ttl?: number): Promise<T> {
const cached = clickupCache.get(path);
if (cached) return cached as T;
const data = awaitclickupRequest(path);
clickupCache.set(path, data, ttl ? { ttl } : undefined);
return data as T;
}
// Hierarchy data: 5 min cache (default)const spaces = awaitcachedRequest(`/team/${teamId}/space?archived=false`);
// Task data: 30 sec cache (changes more often)const task = awaitcachedRequest(`/task/${taskId}`, 30_000);
2. Efficient Pagination
Get Tasks returns max 100 tasks per page. Use async generators for memory efficiency.
asyncfunction* paginateTasks(listId: string, filters: Record<string, string> = {}) {
let page = 0;
let hasMore = true;
while (hasMore) {
const params = newURLSearchParams({
page: String(page),
archived: 'false',
subtasks: 'true',
...filters,
});
const data = awaitclickupRequest(`/list/${listId}/task?${params}`);
const tasks = data.tasks;
for (const task of tasks) {
yield task;
}
// ClickUp returns fewer than 100 tasks on last page
hasMore = tasks.length === 100;
page++;
}
}
// Process tasks without loading all into memorylet count = 0;
forawait (const task ofpaginateTasks('900100200300', { 'statuses[]': 'in progress' })) {
awaitprocessTask(task);
count++;
}
console.log(`Processed ${count} tasks`);
3. Connection Pooling
import { Agent } from'node:https';
const keepAliveAgent = newAgent({
keepAlive: true,
maxSockets: 10,
maxFreeSockets: 5,
timeout: 30_000,
scheduling: 'lifo',
});
// Use with undici or node-fetch that supports custom agents// Native fetch in Node 18+ uses keep-alive by default