Blog · June 5, 2026 · ~10 min read

How to track billable hours as a freelancer: the two-job problem

Freelance time tracking is actually two separate jobs. The first job is logging hours accurately for your own billing records. The second job is making those hours visible to the client in a form they can actually use. Most time trackers solve the first job completely and the second job not at all — and most freelancers spend years trying to fix that by adding features to their time tracker.

Where the confusion starts

When a freelancer starts billing by the hour, the first tool they reach for is a time tracker. This is the right instinct. A time tracker solves the logging problem: start a timer when you begin a task, stop it when you finish, export a CSV at the end of the month, and the invoice figures write themselves. Toggl, Harvest, Clockify, and a dozen other options all do this well. The mechanics differ; the output is the same — a reliable record of how you spent your billable time.

The problem shows up slightly later, after the first retainer deal is in place. The client — who is now paying a monthly fee for a set number of hours — starts asking questions. Not about the invoice, which is already paid, but about the allocation: how many hours have been used so far this cycle? How many are left? What were they spent on? These questions are reasonable. The client has purchased a fixed quantity of your time and wants to know how much of it is still available before they make another request.

At this point, the freelancer opens their time tracker and tries to answer the question from it. Sometimes this works, in the sense that they can find the data and read it back to the client. But it doesn’t scale. As retainer clients multiply, each status question requires another lookup, another summary email, another five minutes of context-switching away from billable work. The time tracker is giving correct data — it just isn’t shaped for the question the client is asking.

Job 1: logging hours accurately for billing records

The first job in billable-hours tracking is for you, not for the client. You need an accurate record of what you worked on, when, and for how long, so that you can invoice confidently and defend any disputed charge. This job has a few requirements:

Granularity at the task level. A single timer entry for “client work” covering four hours is not useful for billing. You need task-level entries: “API integration debugging, 2.5h” and “onboarding call, 1h” and “landing page copy revisions, 0.5h.” When a client questions an invoice, task-level detail is what turns a dispute into a quick conversation. When a client doesn’t question the invoice, task-level detail builds the background confidence that makes renewal automatic.

Consistency over precision. The most common failure in time tracking for billing isn’t dishonesty — it’s under-logging. A freelancer handles a quick client Slack message, a five-minute browser lookup on their behalf, a two-paragraph email with specific research behind it. None of these get logged because individually they feel too small. Cumulatively, over a month, they add up to two or three hours of uncharged work per retainer client. Consistent tracking at the “good enough” level beats intermittent tracking at the “perfect” level every time. The goal is a complete record, not a forensic one.

An export path to invoice. The tracker needs to produce something you can reference when writing the invoice. For most freelancers this is a CSV export filtered by client and date range. The CSV either feeds directly into the invoice (if you have a billing tool with import support) or serves as the raw material for a manual line-item summary. Either way, the tracker’s primary output is a billing record, not a client communication artifact.

Any major time tracker handles this job. Toggl Track, Harvest, Clockify, TimeCamp, Paymo — they all give you timer-based entry, task labels, client tagging, and CSV export. The choice between them is mostly a matter of workflow preference and which integrations matter to your stack. For job 1 alone, the tool is not the constraint.

Job 2: making hours visible to the client

The second job is fundamentally different. It’s not about logging time. It’s about communicating the state of the retainer — specifically, the current allocation: how many hours the client has bought this cycle, how many have been used, what they were used for, and when the cycle resets.

The client’s question — “how many hours do I have left?” — is not a request for a billing record. It’s a request for a status check. They want to know their current balance before they decide whether to make a new request. That decision happens in real time, when they’re already thinking about something they want done. They need an answer in ten seconds, not in the time it takes you to reply to an email.

The format mismatch is the root of the problem. Your time tracker is organized around your workflow: client + project + date range. The client’s question is organized around the retainer cycle: total allocated, used so far, remaining, what each entry covered. Those aren’t the same thing, and no amount of configuring your tracker’s reporting settings will make them the same thing, because the underlying data models are different.

What job 2 actually requires is a persistent artifact the client can access without going through you. Not a monthly PDF email you send at the end of the cycle (they’re asking mid-cycle). Not a shared spreadsheet you update when you remember to (they don’t trust the freshness). Not a view inside your time tracker that requires a client login (they won’t set it up, and they shouldn’t have to). The right format is a URL — one the client bookmarks at the start of the retainer and opens whenever the question occurs to them.

Why time trackers solve job 1 and miss job 2

This isn’t a criticism of time trackers. They’re built for a specific purpose: helping the person doing the work keep a billing record. Every major tracker is optimized for that workflow. The timer UI, the tagging system, the reporting dashboards, the CSV export — all of these are shaped for the freelancer’s billing job. Clients aren’t the intended audience.

The shared report feature that most trackers include — Toggl has it, Harvest has it, Clockify has a free-tier version — is the closest any of them get to job 2. But shared reports have a structural problem in the retainer context: they’re filtered by date range, not by billing cycle. If you share a Toggl report filtered to “this month,” the client sees all your tracked time for July 1–31, not the 0–20 hours of their specific retainer. The retainer cycle might run July 15–August 14. The date-range filter and the billing cycle are rarely aligned, and when they drift, the shared report becomes misleading.

A second structural problem: shared reports are snapshots, not live URLs. The client who bookmarks the link gets a static view of the data at the moment the link was generated, not the current state of their retainer. For job 2 to work — for the client to be able to check their hours at any moment and trust that the number is current — the URL has to reflect real-time data from the tracker, not a frozen export.

The broader billing software category has the same gap. FreshBooks, Wave, Bonsai, HoneyBook — they all handle invoice generation well, and some of them include time tracking. But the period between invoices, when the client is wondering how much of their monthly allocation is left, is invisible in all of them. Billing software is built around the invoice event (end of cycle, or beginning of next cycle). The in-cycle status window, which is when clients are actually making scope decisions, isn’t addressed.

The failure mode when you conflate the two jobs

When a freelancer doesn’t separate these two jobs, the natural response to the client’s status questions is to try to solve them with the existing tool. They explore the tracker’s reporting options. They try different sharing permissions. They set up a weekly scheduled report email. They build a client-facing view inside a shared Notion page that they manually update from the tracker export.

Each of these attempts solves the problem partially and introduces a new friction: the scheduled email arrives on a fixed cadence, not when the client is thinking about a request. The shared Notion page is only as current as the last time the freelancer remembered to update it. The custom reporting view requires the client to log into the tracker with an account they didn’t ask for and don’t maintain.

Meanwhile, the status questions continue arriving. Not because the freelancer is failing to communicate — they’re communicating more than ever, through more channels than ever — but because none of the channels give the client the on-demand, always-current answer they actually need. The client who can’t self-serve defaults to email. That’s not a client behavior problem. It’s a system design problem.

The symptom that should trigger a reassessment: if you’re manually updating any artifact — a spreadsheet, a Notion page, a status email — to keep the client informed of their hours, you’re doing job 2 manually. Manual job-2 work accumulates to 2–4 hours per month per retainer client at scale. Three retainer clients means most of a workday per month on status admin that could be a URL.

Setting up both jobs properly

The right setup separates the two jobs completely, with a distinct tool for each.

Job 1 (logging for billing records): use whatever time tracker you already have. If you don’t have one, pick any of the major options. The choice is not the constraint; the habit is. Log at the task level. Log the small things (client communications, quick lookups, async replies that involve research). Export a CSV at the end of each billing cycle. This becomes the authoritative record for the invoice and the source of truth for job 2.

Job 2 (client-facing visibility): the tracker’s CSV export becomes the input to something shaped for the client’s question rather than the freelancer’s billing workflow. The format the client needs is a progress bar and a work log: how many hours used out of the total cap, a reset date, and a list of what each entry covered — presented at a URL the client can bookmark and check without logging in. The tracker solves the data collection problem. A separate tool solves the presentation problem.

The separation has a second benefit: it decouples the two jobs’ cadences. Job 1 updates continuously as you log time throughout the cycle. Job 2 updates when you import the latest tracker export — once a day or a few times a week, depending on your workflow. The client sees a current-enough view without requiring you to maintain a live integration. And because job 2 has its own tool, changes to your time tracker (switching from Toggl to Harvest, adopting a new workflow for logging) don’t break the client-facing layer.

What changes after the separation

The most immediate change is that the hours question stops arriving as an email. Clients who have a URL they can check at any moment don’t need to ask you. They check the URL when the question occurs to them, see the current balance and work log, and make their request decision without any involvement from you. This isn’t a marginal improvement in email volume — for most freelancers with retainer clients, it eliminates the category of status email almost entirely.

The second change is what the freed communication capacity gets redirected to. Monthly check-ins that used to open with “here’s what I worked on this month” (a status update the client now has passively) shift to “based on what we delivered, here are the three things I think would have the highest impact next month.” The meeting becomes a planning conversation rather than a reporting session, which is where the relationship’s strategic value lives.

A third, subtler change: the client’s perception of the retainer’s value stops depending on the frequency of communication. A client who has passive visibility into the hours log knows that work is happening even when they don’t hear from you. A client without that visibility perceives quiet weeks as low-activity weeks, regardless of what the tracker actually shows. This perception gap is what makes renewal conversations harder than they should be: the client has calibrated their value assessment to interaction frequency, not to delivered work. Passive visibility fixes that calibration.

The two-job framework in practice

When a new retainer starts, the setup has two steps. First, tag the client correctly in your time tracker so that all billable entries against their account can be exported as a clean CSV at cycle end. Second, create a live URL for that client that shows their hours allocation, current usage, and cycle reset date — and send them the URL in the first week, before any hours have been logged, so they learn the format in a low-stakes moment.

After that, the two tools run in parallel without interaction. You log time as usual. You import the tracker CSV into the client-facing layer on whatever cadence fits your workflow (daily, every few days, weekly). The client checks their URL when they think of it. The email thread that used to carry status questions carries nothing, because the question never forms into an email.

This is the right shape for billable-hours tracking at the retainer level. Not one tool trying to serve both jobs, but two tools, each built for the job it actually does — with a clean handoff between them.


HourTab handles job 2. Import your time tracker’s CSV export and HourTab generates a public URL showing your client their current hours balance, the work log, and the cycle reset date — no client login required. Send the URL once at the start of the retainer; the client bookmarks it and checks it when the question comes up. The status email stops. See how it works.