Rowduet

Export Jira Worklogs to Google Sheets for Timesheets

Updated · 8 min read

A timesheet needs one row per worklog: who logged time, on which day, against which issue, and for how long. Jira’s standard exports give you one row per issue, so the job is turning worklogs into rows. The free routes, from least to most setup: Jira’s “all fields” CSV export, where worklogs arrive as repeated Log Work columns that one formula can flatten; Atlassian’s Jira Cloud for Sheets add-on, which has worklog fields and a known duplicates bug; or a short Apps Script that reads every worklog from Jira’s API and filters by the day the work happened.

If you bill clients from the result, use the script. Details on each route follow, as of October 2026.

Three things that make worklog reports go wrong

  • Started date versus created date. Each worklog has a started time, the day the person says they did the work, and a created time, when they typed it in. Someone who logs Friday’s work on Monday creates it in the next week. Bill by the started date.
  • JQL finds issues, not worklogs. worklogDate >= startOfMonth() returns every issue with at least one entry this month, including that issue’s older entries. Atlassian also notes that worklog searches only look at the most recent 1,000 worklogs on a work item.
  • API responses embed only 20 worklogs per issue. The worklog field in Jira’s issue responses stops at 20 entries, a long-standing behavior reported in JRACLOUD-34746 and closed as “Answered”; users report the same cap in search results. Scripts have to call the per-issue worklog endpoint.

Compare the options

Method Cost One row per worklog Filters by work date Watch for
CSV export, all fields Free After one formula In the sheet Users find worklogs only in the all-fields export; large, slow files
Jira Cloud for Sheets Free Check your output In the sheet Duplicate entries bug, past field outages
Project Aid for Jira Free Per its time reports Per its time reports Listing mentions no schedule and doesn’t state Cloud or Server
Apps Script + REST API Free Yes Yes You maintain it; tokens expire yearly
Timesheet apps in Jira, such as Tempo Paid Yes Yes Some keep data outside Jira’s worklogs

Method 1: The all-fields CSV export

  1. Open Filters > Search work items, switch to JQL and run a query that covers the period, for example project = ABC AND worklogDate >= startOfMonth("-1") AND worklogDate < startOfMonth().
  2. Choose More actions (•••) > Export > Export CSV (all fields). Users report that worklog columns only appear in the all-fields export, not when you export current fields. Atlassian warns that all-fields exports are slow and produce large files, and each export stops at 10,000 work items.
  3. In Google Sheets, choose File > Import > Upload and import the file into a tab named Raw.

Each worklog lands in its own column headed “Log Work”, so an issue with 12 entries fills 12 columns. In exports users have shared, each cell holds the comment, date, author and seconds, separated by semicolons, which is also the order Atlassian’s CSV import documentation uses for worklogs.

To get one row per entry, find the Issue key column and the first and last Log Work columns. With the key in column B and Log Work in columns AF to AQ:

=ARRAYFORMULA(REGEXEXTRACT(TOCOL(IF(Raw!AF2:AQ = "", NA(), Raw!B2:B & ";" & Raw!AF2:AQ), 2), "(?s)^([^;]*);(.*);([^;]*);([^;]*);([^;]*)$"))

TOCOL with 2 drops the empty cells, and the pattern reads the last three parts from the right, so semicolons inside a comment don’t break it. You get key, comment, date, author and seconds. Divide seconds by 3,600 for hours.

Before you trust it, compare a few rows with the work item’s Work log in Jira. In one Atlassian Community thread, exported dates differed from what Jira showed, which a responder put down to time zones. Also check whether authors appear as names or account IDs. If the dates aren’t recognized as dates, convert them before you filter by month.

Method 2: Jira Cloud for Sheets

Atlassian’s free add-on lists worklog fields in its column picker. Two known problems as of October 2026:

  • Duplicates. JRACLOUD-94928, opened June 2025 and still “Gathering Impact”, reports duplicate worklog entries when you import with the worklog fields selected. The documented workaround is removing duplicates, in Sheets with Data > Data cleanup > Remove duplicates. Be careful: two identical genuine entries would also be removed.
  • Empty fields. Users reported Work Log fields coming back empty after the December 2024 update. An Atlassian staff member said on January 16, 2025 that the problems had been resolved, but users reported new field breakage later that year. Our Jira Cloud for Sheets troubleshooting guide covers what to try.

Check whether the import gives you one row per issue or per worklog before you build formulas on it.

A third free option is Project Aid for Jira, a Sheets add-on with 308K+ installs whose listing (updated February 10, 2026) offers to “Create Time Reports for specific Jira users based on their worklogs.” Its listing doesn’t mention scheduled refresh.

Method 3: Apps Script and the Jira REST API

The script below finds every issue with time logged last month, reads each issue’s worklogs from /rest/api/3/issue/{key}/worklog (filtered by start time, paged until done), and writes one row per entry. It keeps only entries that started inside the month, so JQL and time zone edge cases don’t leak in.

Setup:

  1. Create an API token at id.atlassian.com/manage-profile/security/api-tokens with Create API token (not the scoped option, so the script can call your site URL). New tokens expire after at most a year; put the date in your calendar.
  2. Open Extensions > Apps Script. Under Project Settings, add script properties JIRA_SITE (like your-site.atlassian.net), JIRA_EMAIL and JIRA_TOKEN. Anyone who can edit the sheet can read them.
  3. Paste the script, replace ABC with your project key, and run importWorklogs once to authorize it.
  4. Under Triggers, add a time-driven month timer for the first day of each month.
function importWorklogs() {
  const props = PropertiesService.getScriptProperties();
  const site = props.getProperty('JIRA_SITE');
  const auth = 'Basic ' + Utilities.base64Encode(
    props.getProperty('JIRA_EMAIL') + ':' + props.getProperty('JIRA_TOKEN'));
  const tz = Session.getScriptTimeZone();
  const day = function (d) { return Utilities.formatDate(d, tz, 'yyyy-MM-dd'); };

  // Last calendar month. JQL gets one extra day each side; the script trims.
  const now = new Date();
  const from = new Date(now.getFullYear(), now.getMonth() - 1, 1);
  const to = new Date(now.getFullYear(), now.getMonth(), 1);
  const jql = 'project = ABC AND worklogDate >= "' + day(new Date(from - 864e5)) +
    '" AND worklogDate <= "' + day(to) + '"';

  const rows = [['Date', 'Person', 'Key', 'Summary', 'Hours', 'Comment']];
  searchIssues(site, auth, jql, ['summary']).forEach(function (issue) {
    let startAt = 0;
    let page;
    do {
      page = getJson(site, auth, '/rest/api/3/issue/' + issue.key + '/worklog?startAt=' +
        startAt + '&startedAfter=' + from.getTime() + '&startedBefore=' + to.getTime());
      page.worklogs.forEach(function (w) {
        const started = new Date(w.started);
        if (started < from || started >= to) return;
        rows.push([day(started), w.author ? w.author.displayName : '', issue.key,
          issue.fields.summary, w.timeSpentSeconds / 3600, adfText(w.comment).trim()]);
      });
      startAt += page.worklogs.length;
    } while (page.worklogs.length > 0 && startAt < page.total);
  });
  const ss = SpreadsheetApp.getActive();
  const sheet = ss.getSheetByName('Worklogs') || ss.insertSheet('Worklogs');
  sheet.clearContents();
  sheet.getRange(1, 1, rows.length, rows[0].length).setValues(rows);
}

function searchIssues(site, auth, jql, fields) {
  const issues = [];
  let token = null;
  do {
    let path = '/rest/api/3/search/jql?maxResults=100&fields=' + fields.join(',') +
      '&jql=' + encodeURIComponent(jql);
    if (token) path += '&nextPageToken=' + encodeURIComponent(token);
    const data = getJson(site, auth, path);
    Array.prototype.push.apply(issues, data.issues);
    token = data.nextPageToken;
  } while (token);
  return issues;
}

function getJson(site, auth, path) {
  const res = UrlFetchApp.fetch('https://' + site + path, {
    headers: { Authorization: auth, Accept: 'application/json' },
    muteHttpExceptions: true,
  });
  if (res.getResponseCode() !== 200) {
    throw new Error('Jira returned ' + res.getResponseCode() + ': ' +
      res.getContentText().slice(0, 300));
  }
  return JSON.parse(res.getContentText());
}

function adfText(node) {
  if (!node) return '';
  let text = node.text || '';
  (node.content || []).forEach(function (child) { text += adfText(child); });
  return node.type === 'paragraph' ? text + '\n' : text;
}

Worklog comments come back in Atlassian Document Format, so adfText flattens them to plain text. Dates use the script’s time zone, set under Project Settings; match it to the time zone you bill in. Google stops each run after 6 minutes, so for large projects narrow the JQL or split by project.

Turn the rows into a timesheet

A person-by-day grid for the month, on a new tab:

=QUERY(Worklogs!A:F, "select B, sum(E) where E > 0 group by B pivot A", 1)

Hours per person per issue, ready for an invoice:

=QUERY(Worklogs!A:F, "select B, C, sum(E) where E > 0 group by B, C order by B label sum(E) 'Hours'", 1)

Two helper columns are often worth adding next to the data: the project key with =REGEXEXTRACT(C2, "^[A-Z0-9_]+") if projects map to clients, and billable hours rounded up to the quarter hour with =CEILING(E2, 0.25), if your contracts say so.

If your team logs time in Tempo

Tempo’s knowledge base says that on Cloud, Tempo keeps its own database. Jira holds synced copies of worklogs with Jira fields only, while Tempo’s accounts, work attributes and billable hours stay in Tempo. If you bill from those, pull them from Tempo’s REST API (api.tempo.io/4) instead of Jira’s.

Check totals before you send an invoice

  1. Pick three issues and compare their Work log in Jira with your rows.
  2. Look for duplicate rows, especially from add-on imports.
  3. Check entries on the first and last day of the month for time zone shifts.
  4. Re-run the import just before invoicing, then copy the month’s rows to a separate tab as values. Worklogs can be edited or deleted later, and a refresh would change last month’s numbers.

For other report queries, including worklog searches by team, see our JQL examples for reports. For other ways to bring Jira data into Sheets, see Jira Cloud for Sheets alternatives.

Where Rowduet fits

Rowduet (in development) is a Google Sheets add-on being built to import Jira fields that the free add-on struggles with, worklogs included, on an hourly or daily schedule, with an email when a refresh fails. It isn’t available yet. If you build timesheets or client reports from Jira every month, you can join early access.

Frequently asked questions

Can Jira Cloud for Sheets export worklogs?

It offers worklog fields, but Atlassian's tracker has an open bug, JRACLOUD-94928, about duplicate worklog entries in imports, and users reported empty worklog fields after the December 2024 update. Remove duplicates and compare totals with Jira before you bill from it.

How do I find Jira issues with time logged last month?

Use worklogDate >= startOfMonth("-1") AND worklogDate < startOfMonth() in JQL. It returns issues, not individual worklogs, so a timesheet still has to filter each entry by the date the work started.

Why don't my worklog totals match Jira?

The usual causes are filtering by when an entry was created instead of when the work started, time zone differences at the edges of the period, duplicate rows from an import, and issue search embedding only the first 20 worklogs per issue. Read worklogs from the per-issue worklog endpoint and filter on the started date.

Does Jira Cloud have a built-in timesheet report?

Not a per-person, per-day timesheet. Jira Cloud shows time spent and estimates per work item, and Atlassian Community answers to this question point to Marketplace apps such as Tempo, a worklog export, or the REST API.