Pricing

How to Connect ClickUp to Google Sheets

ClickUpSaved SearchDateDocumentAmount07/01INV-40214,280.0007/02INV-40221,860.5007/05INV-40239,140.0007/08INV-40242,375.2507/09INV-40256,902.7507/12INV-40263,118.40BrookedABCDateDocumentAmount123456707/01INV-40214,280.0007/02INV-40221,860.5007/05INV-40239,140.0007/08INV-40242,375.2507/09INV-40256,902.7507/12INV-40263,118.40Google SheetsClickUp report
JW
James Whitfield

Export ClickUp tasks to Google Sheets, including the closed tasks and subtasks left out by default and the timestamps that arrive as millisecond strings.

ClickUp's API is capable, and two defaults quietly ruin the first report people build with it: closed tasks and subtasks are both excluded unless you explicitly ask for them, so completed-work reporting comes back near empty. Add timestamps that arrive as millisecond strings and a four-level hierarchy you have to walk to find a list ID, and there is enough here to be worth spelling out.

The two-minute option: CSV export

Open a list or board view, use the view's ••• menu, and choose Export with CSV. The export reflects the current view, so its filters, grouping and visible columns all apply. Import into Sheets via File → Import.

ClickUp to Google Sheets for free (Apps Script + API v2)

Generate a personal API token under Settings → Apps → API Token. It begins pk_ and goes in the Authorization header directly, with no Bearer prefix, which trips up people used to other APIs.

Get the list ID from the list's URL. Then the important part: pass include_closed=true and subtasks=true, or your export silently omits most of the work you are trying to measure.

Code.gs
function importClickUpTasks() {  const TOKEN = "pk_XXXXXXXXXXXX";  const LIST_ID = "901234567";   // No "Bearer" prefix; the token goes in the header as-is.  const headers = { Authorization: TOKEN };   const rows = [];  let page = 0;   while (true) {    const url = "https://api.clickup.com/api/v2/list/" + LIST_ID + "/task"      + "?page=" + page      + "&include_closed=true"   // closed tasks are excluded by default      + "&subtasks=true";        // subtasks are excluded by default too     const res = JSON.parse(      UrlFetchApp.fetch(url, { headers, muteHttpExceptions: true })        .getContentText()    );    if (res.err) throw new Error(res.err);    if (!res.tasks || !res.tasks.length) break;     res.tasks.forEach((t) => {      // Custom field values are typed; dropdowns give an index into      // type_config.options rather than the label itself.      const custom = (t.custom_fields || [])        .filter((f) => f.value !== undefined && f.value !== null)        .map((f) => {          let v = f.value;          if (f.type === "drop_down" && f.type_config && f.type_config.options) {            const opt = f.type_config.options[v];            v = opt ? opt.name : v;          }          return f.name + ": " + v;        })        .join("; ");       rows.push([        t.name,        t.status ? t.status.status : "",        (t.assignees || []).map((a) => a.username).join(", "),        t.priority ? t.priority.priority : "",        msToDate(t.due_date),         // epoch milliseconds, as a string        msToDate(t.date_created),        msToDate(t.date_closed),        t.time_estimate ? t.time_estimate / 3600000 : "",  // ms → hours        custom,        t.url,      ]);    });     // last_page tells you when to stop; pages are 100 tasks each.    if (res.last_page) break;    page++;  }   const header = ["Task","Status","Assignees","Priority","Due","Created",                  "Closed","Est. hours","Custom fields","URL"];  const ss = SpreadsheetApp.getActiveSpreadsheet();  const sheet = ss.getSheetByName("ClickUp") || ss.insertSheet("ClickUp");  sheet.clearContents();  sheet.getRange(1, 1, 1, header.length).setValues([header]);  if (rows.length) {    sheet.getRange(2, 1, rows.length, header.length).setValues(rows);  }} // ClickUp sends epoch milliseconds as strings: "1721476800000".function msToDate(value) {  if (!value) return "";  return new Date(Number(value));}

Note time_estimate and the time-tracking fields are also in milliseconds, so dividing by 3,600,000 gives hours. Treating them as minutes or seconds is a common source of estimates that look absurd.

Limits: around 100 requests a minute per token on lower plans, and Apps Script's six-minute cap. Covering a whole workspace means walking spaces, folders and lists before you fetch any tasks, so target specific lists rather than crawling everything on each run.

The Brooked method

Brooked walks the hierarchy for you so you pick spaces and lists from a menu rather than hunting for IDs, includes closed tasks and subtasks by default, converts millisecond timestamps into real dates, and resolves dropdown custom fields to their labels. Several lists can import into one spreadsheet on a single schedule.

Step 1: Install Brooked

Install Brooked from the Google Workspace Marketplace. Free tier includes 100 imports per month, no credit card required.

Step 2: Connect ClickUp

Choose ClickUp in Brooked and paste a personal API token from Settings → Apps. Pick the space and lists you want, decide whether to include closed tasks and subtasks, and import.

Step 3: Schedule the refresh

Set the import to re-run daily or weekly so sprint and throughput reports rebuild before the review rather than during it.

BrookedBrooked AI, ClickUp
Live
Ask anything about your ClickUp data…

Method comparison

MethodCostSetupRefreshTwo-wayBest for
ClickUp CSV exportFree2 minManualNoA one-off export of a single view
Apps Script + API v2Free25 to 35 minTime-driven triggerDIYSeveral lists, custom fields
Zapier / MakeFree tier limited · paid for volume10 to 15 minOn task created or updatedDIY (second workflow)Logging individual task events
BrookedFree tier · Pro $29/user/mo~3 minScheduled (hourly, daily, weekly)YesScheduled reporting across spaces and lists

Common use cases

  • Sprint throughput: tasks closed per person per sprint across lists
  • Estimate accuracy: compare time_estimate against tracked time to calibrate planning
  • Portfolio status: roll several spaces into one leadership table
  • Client reporting: filter to client-facing lists and share only that sheet
  • Workload: open tasks per assignee to spot who is overloaded

Troubleshooting common issues

Frequently asked questions

Why are closed ClickUp tasks missing from my export?

The task endpoint excludes closed tasks by default, so any report about completed work comes back looking empty. Add include_closed=true to the request. Subtasks are excluded by default too and need subtasks=true, which together account for most reports where the numbers look far too low.

Why do ClickUp dates show as huge numbers?

ClickUp returns timestamps as Unix epoch milliseconds, and as strings rather than numbers. Convert with new Date(Number(value)) before writing to a sheet. Because they are strings, arithmetic on them silently concatenates rather than adding, which produces nonsense durations.

Where do I find my ClickUp list ID?

ClickUp nests everything as team, then space, then folder, then list, and the task endpoint needs the list ID specifically. The quickest route is the list URL, which contains it. Programmatically you walk the hierarchy: team, then spaces, then folders and folderless lists, which is several calls before you can request a single task.

Can I export ClickUp to Google Sheets for free?

Yes. ClickUp exports a view to CSV from the view menu on all plans, though export options are richer on paid tiers. The Apps Script method here uses a free personal API token and a free time-driven trigger. Brooked's free tier covers 100 imports a month.

How do custom fields come back?

As an array on each task, with each entry carrying the field name, its type, and a value whose shape depends on that type. Dropdowns return an option index or ID rather than the label, so you need the field's type_config options list to resolve the readable value. Labels and users come back as arrays of objects.

What are ClickUp's rate limits?

Roughly 100 requests per minute per token on Free and Unlimited plans, with higher ceilings on Business and Enterprise. Because the hierarchy walk plus per-list task requests adds up quickly on a large workspace, prefer filtered requests over pulling every list, and space out scheduled runs.

Connect ClickUp to Google Sheets today.

Free tier: 100 imports per month, no credit card required. Or see the full ClickUp integration details.

Install Brooked free →

Also in Project Management

More Project Management guides

Project Management

How to Connect Monday.com to Google Sheets

Export Monday.com boards to Google Sheets with the GraphQL API, including the items query that was removed and the complexity budget that throttles large boards.

JW
James Whitfield
Read
Project Management

How to Connect Clockify to Google Sheets

Export Clockify time entries to Google Sheets, including the per-user endpoint that hides your team's hours and the ISO durations that will not sum.

JW
James Whitfield
Read
Project Management

How to Connect Asana to Google Sheets

Export Asana tasks to Google Sheets with the API, including the opt_fields parameter without which you get only names, and custom fields that arrive typed.

JW
James Whitfield
Read

Get your spreadsheet hours back

Brooked sets up in seconds. Your team is querying live data before lunch.

Get started free