Tracking billable hours on Toggl Track's Free plan
Toggl Track's Free plan doesn't let you mark time as billable or set hourly rates. You can still bill from it. Here's what Free actually includes, and four practical ways to get from tracked time to hours × rate.
The short answer
- Not on Free: the billable flag on time entries, billable rates, rounding, and CSV/Excel export. They start on Toggl's Starter plan.
- On Free: projects with clients, unlimited tags, the Summary and Detailed reports with filters by client, project or tag, PDF export, invoices from a report (with Toggl's logo), and the Toggl API.
- So: mark billable work yourself (a
billabletag, or keeping non-billable work in its own project), filter the Summary report, and multiply hours by your rate, by hand or with a free script in Google Sheets. Toggl's own support team suggests calculating the billable total manually on Free (Toggl Community).
What Toggl Track Free includes for billing
| Feature | On Free? | Source |
|---|---|---|
| Projects, clients and tags | Yes. The pricing comparison lists unlimited projects and unlimited tags on Free. | Pricing, Tags |
| Billable status on a time entry (the $ icon) | No, "paid plans only" | Creating a time entry |
| Billable rates | No, "a Starter plan feature" | Billable rates, Payment plans |
| Rounding in reports | No, Starter and up | Payment plans, Pricing |
| Summary and Detailed reports | Yes (the pricing page calls the Detailed report "Time Logs") | Pricing |
| Report filters | Client, project, tag and description: yes, with the is condition. Is not is a Starter feature. Filtering by billable status or rate is a paid feature. | Using filters |
| Export a report | PDF only. "CSV and Excel exports are a paid plan feature." | Summary report, Exporting data |
| Create an invoice from a report | Yes. On Free the downloaded PDF carries the Toggl logo. | Creating invoices |
| Toggl API | Yes, limited to 30 requests per hour, per user, per organization | Pricing, API limits |
| Team size | "A limited number of users". The pricing page's FAQ says up to 3. | Pricing, Payment plans |
Two notes. Toggl's old Track pricing address now redirects to a single pricing page for Toggl's combined product, so that's the one we cite. And that page isn't fully consistent: its FAQ lists "tags" among what you get by upgrading, while its comparison table shows unlimited tags on Free and the Tags help article mentions no plan limit. We've gone with the table and the help article. If your Free workspace behaves differently, please tell us.
1. Mark billable work with a tag or a project
Without the billable flag, you need your own way to tell billable time from the rest. Two approaches work on Free:
A billable tag
Create a tag called billable and add it to every entry you'll charge for. A shortcut from Toggl's help: type # in the description
to add a tag while you track (Toggl).
Tag the billable work, not the non-billable work. On Free, a report filter can say Tag is billable, but Tag is not … is a Starter feature (Toggl: Using filters). With a positive tag you can always filter down to exactly what you'll bill.
Separate projects or clients
If every client project is fully billable, the client itself is the marker. Put admin, sales and other internal work in a project with no client (or a client called "Internal"), then filter reports by client. This also suits invoicing, since one invoice usually means one client.
You can combine the two: filter by client and by the billable tag, for clients where only some of the work is chargeable.
2. Read the totals off the Summary report
- Open Reports → Summary and set the date range to your billing period (Toggl: Summary report).
- Add filter: Client is …, plus Tag is billable if you use the tag.
- Read Total Hours from the summary bar. The grouping list below breaks it down further, for example by project.
- Multiply by your rate. If you round time, round each entry yourself, since report rounding is a Starter feature.
Getting the numbers out: on Free you can export the report as a PDF, but not as CSV or Excel. You can also turn the report into an invoice (Export → Create invoice). From the Summary report that makes one line per project, with total time and amount. On Free the PDF has the Toggl logo (Toggl: Creating invoices). Because Free has no billable rates, check what the amount column shows before you send anything. We haven't tried this on a Free workspace yet.
Keeping track of what you've billed: Toggl's invoice article suggests tagging billed entries paid and filtering next time with Tags is not paid
(Toggl). On Free that filter condition isn't available (is not is Starter), so bill fixed periods instead, such as each calendar month,
and note the last period you invoiced.
3. Pull entries into Google Sheets with a free script
Free can't export CSV, but the Toggl API is included. Our free import script copies your own entries from the last 30 days into a tab called "Toggl", with their tags and client names. It uses one API request per run.
The script below goes with it. It reads that "Toggl" tab and writes a Billable hours tab with one row per client and project: entries, total hours,
billable hours (entries with your billable tag, or with Toggl's billable flag if you're on a paid plan), your rate and the amount.
It can round each billable entry up to 6 or 15 minutes, and it makes no requests to Toggl, so it doesn't touch your API limit.
/**
* Billable hours by tag, for Toggl Track Free (no billable flag, no rates).
* Free, no add-on, and it makes no requests to Toggl at all.
*
* It reads the "Toggl" tab written by importTogglEntries() (the free script in
* our "5 ways to get Toggl Track data into Google Sheets" guide) and writes a
* "Billable hours" tab with one row per client and project:
* entries, total hours, billable hours, rate, amount.
* An entry counts as billable if it has the BILLABLE_TAG tag (any letter case)
* or if Toggl marked it billable (paid Toggl plans).
*
* Setup: paste below importTogglEntries() in the same Apps Script project,
* edit the settings, then run summarizeBillableHours after each import.
*/
var BILLABLE_TAG = 'billable'; // the tag you add in Toggl to work you bill for
var ROUND_UP_TO_MINUTES = 0; // e.g. 6 or 15 rounds each billable entry up; 0 = no rounding
var RATES = { // hourly rate per client, in your invoice currency
'Example Client': 50 // made-up example: replace with your own clients
};
var DEFAULT_RATE = 0; // rate for clients not listed above; 0 = leave the amount blank
function summarizeBillableHours() {
var ss = SpreadsheetApp.getActive();
var src = ss.getSheetByName('Toggl');
if (!src) throw new Error('No "Toggl" tab yet. Run importTogglEntries() first.');
var values = src.getDataRange().getValues();
var head = values[0] || [];
var col = {};
['Start', 'Stop', 'Hours', 'Project', 'Client', 'Tags', 'Billable'].forEach(function (h) {
col[h] = head.indexOf(h);
if (col[h] < 0) throw new Error('The "Toggl" tab has no "' + h + '" column. Re-run importTogglEntries().');
});
var tag = String(BILLABLE_TAG).trim().toLowerCase();
var groups = {};
for (var i = 1; i < values.length; i++) {
var r = values[i];
var client = String(r[col.Client] || '(no client)');
var project = String(r[col.Project] || '(no project)');
var minutes = entryMinutes(r[col.Start], r[col.Stop], r[col.Hours]);
var tags = String(r[col.Tags] || '').split(',').map(function (t) { return t.trim().toLowerCase(); });
var billable = tags.indexOf(tag) >= 0 || String(r[col.Billable]).toLowerCase() === 'yes';
var key = client + '\u0000' + project;
var g = groups[key] || (groups[key] = { client: client, project: project, n: 0, total: 0, billable: 0 });
g.n++;
g.total += minutes;
if (billable) g.billable += roundUp(minutes, ROUND_UP_TO_MINUTES);
}
var rows = [];
var sum = { n: 0, total: 0, billable: 0, amount: 0, rated: false, missingRate: false };
Object.keys(groups).sort().forEach(function (k) {
var g = groups[k];
var rate = Object.prototype.hasOwnProperty.call(RATES, g.client) ? RATES[g.client] : DEFAULT_RATE;
var hours = round2(g.billable / 60);
var amount = rate > 0 ? round2(hours * rate) : '';
if (hours > 0 && amount === '') sum.missingRate = true;
var total = round2(g.total / 60);
// Totals add up the rounded row values, so they match what you see.
sum.n += g.n; sum.total += total; sum.billable += hours;
if (amount !== '') { sum.amount += amount; sum.rated = true; }
rows.push([guardText(g.client), guardText(g.project), g.n, total, hours, rate > 0 ? rate : '', amount]);
});
if (rows.length) {
rows.push(['Total', '', sum.n, round2(sum.total), round2(sum.billable),
sum.missingRate ? 'some rates missing' : '', sum.rated ? round2(sum.amount) : '']);
}
var out = ss.getSheetByName('Billable hours') || ss.insertSheet('Billable hours');
out.clearContents();
out.getRange(1, 1, 1, 7).setValues([['Client', 'Project', 'Entries', 'Total hours', 'Billable hours', 'Rate', 'Amount']]);
if (rows.length) out.getRange(2, 1, rows.length, 7).setValues(rows);
}
/** Minutes from Start/Stop when both are dates, otherwise from the Hours column. */
function entryMinutes(start, stop, hours) {
if (start && stop && typeof start.getTime === 'function' && typeof stop.getTime === 'function') {
return Math.max(0, (stop.getTime() - start.getTime()) / 60000);
}
return Math.max(0, Number(hours) || 0) * 60;
}
function roundUp(minutes, step) {
return step > 0 ? Math.ceil(minutes / step - 1e-9) * step : minutes;
}
function round2(n) { return Math.round(n * 100) / 100; }
/** Names read back from the sheet are plain text again, so guard them before writing. */
function guardText(s) {
s = s == null ? '' : String(s);
return /^[=+\-@]/.test(s) ? "'" + s : s;
}
- Set up the import script first (see its steps) and run
importTogglEntries. - In the same Apps Script project, add a new script file and paste this in. Change
RATESto your own clients and rates (the client name must match Toggl's exactly; "Example Client" is a made-up placeholder). LeaveDEFAULT_RATEat 0 to leave unknown clients without an amount. - Run
summarizeBillableHours. Re-run it after each import, or add it to the same daily trigger, after the import.
Honest limits:
- It works on whatever the import put in the "Toggl" tab: only your entries from the last 30 days.
- The tag must match exactly, apart from letter case and spaces:
non-billabledoes not count asbillable. - Rates are per client, in one currency of your choosing. There are no project-level rates, and nothing is converted.
- Total hours aren't rounded; billable hours are, if you turn rounding on. The Total row adds up the rounded row values, so it matches what you see.
- We tested it against simulated sheet data and a simulated Toggl API (including running both scripts together), not a live Toggl account. Check the first result against Toggl's Summary report.
- Apps Script quotas apply, for example 6 minutes per run on a consumer Google account (Google). For a month of one person's entries that's far more than enough.
4. Put the hours on an invoice
Toggl's own invoice (step 2) is the quickest. If you'd rather send your own, our free invoice template works with any of the steps above: copy the billable hours and rates into its line items and it works out the amounts, subtotal, optional tax lines and due date. No email or sign-up.
Where Hourly Sheets fits (ours)
Hourly Sheets is a Google Sheets add-on that pulls Toggl Track entries into a sheet and builds a billable summary by client and project and an invoice tab.
It works on Toggl Free. Since there's no billable flag there, you set Count as billable to Entries with tag and type your tag (for example billable),
or to All entries, and set your rates per client or project in its Rates tab. It can round each entry to a billing increment. Free covers the last 30 days with manual sync. Pro is US$6/month (launch pricing).
Tags: with Entries with tag, an entry counts as billable if it has your tag. The match works like the free script above: the exact tag name,
ignoring letter case and spaces, so non-billable doesn't count as billable. You can list several tags separated by commas, and an entry with any of them counts.
In this mode Toggl's own billable flag is ignored; on a paid Toggl plan you'd normally use Entries marked billable in Toggl instead.
The tags are saved in the add-on's entries tab, so changing the tag and clicking Apply rates & rebuild recalculates without using any of your Toggl API requests.
Tag names come with the project and client names it already fetches. At most one extra request per sync is spent, and only when an entry carries a tag created since those names were last fetched.
Limits, honestly: we've tested the tag mode against a simulated Toggl API, not a live Toggl account yet. Tag names that contain a comma can't be used as the billable tag. If you don't tag (or don't want to), All entries still works: keep internal work in a project with no client so it never lands on a client's invoice tab. The free scripts above remain a good fit if you only need totals and don't want an add-on. Product page & pricing · Setup guide
When upgrading Toggl is simply better
If you bill several clients at different rates every month, Toggl's Starter plan adds billable rates, the billable flag, rounding, CSV export and the is not filter (Toggl: Payment plans, Using filters). Toggl's help page lists Starter from $9 per license per month. Check the pricing page for the current price with monthly or annual billing. If your time is worth more than an hour a month of workarounds, that may be the cheaper option.
Sources
All read on 2026-10-05. Toggl changes its plans from time to time, so check the live page before you rely on a feature.
- Toggl pricing and plans: Pricing and plan comparison · Payment plans at Toggl Track
- Toggl help: Creating a time entry · Billable rates · Tags · Using filters in reports · Summary report · Detailed report · Creating invoices · Exporting data · API & webhook limits
- Toggl Community: "Can I just track my total time in the Free plan?" (answer from Toggl Support)
- Google: Apps Script quotas
Found something wrong or out of date? Tell us via Support and we'll fix it and note the change here.