---
title: Importing data from a CSV file (Universal CSV Importer)
description: "This guide walks a workspace admin through importing data into Toggl from any CSV file: an export from Harvest, Clockify, Asana or another tool, or a spreadsheet you built yourself."
---

[Skip to content](https://docs.toggl.com/importing-data-from-a-csv-file-universal-csv-importer#main-content)

English

Show submenu for translations

[![logo-dark](https://docs.toggl.com/hs-fs/hubfs/logo-dark.png?width=223&height=85&name=logo-dark.png)](https://docs.toggl.com/?hsLang=en)

Open main navigation

Close main navigation

- English
  
  Show submenu for translations
- [Go to Toggl](https://focus.toggl.com/)

[Go to Toggl](https://focus.toggl.com/)

 How can we help you?

- There are no suggestions because the search field is empty.

1. [Toggl 2.0 Knowledge Base](https://docs.toggl.com/?hsLang=en)
2. [Getting started](https://docs.toggl.com/getting-started?hsLang=en)
3. [Start here](https://docs.toggl.com/getting-started?hsLang=en#start-here)

# Importing data from a CSV file (Universal CSV Importer)

## This guide walks a workspace admin through importing data into Toggl from any CSV file: an export from Harvest, Clockify, Asana or another tool, or a spreadsheet you built yourself.

Here is a visual step-by-step of the process, which we break down below:

![01\_csv\_import\_steps](https://docs.toggl.com/hs-fs/hubfs/01_csv_import_steps.gif?width=670&height=592&name=01_csv_import_steps.gif)

### How the import works

The importer is a three-step wizard, followed by the import itself. Here's what each step does, so the terms make sense when you meet them in the steps below.

1. **Upload** your CSV files.
2. **Map columns.** The importer shows a **card** for each type of data: Members, Projects, Tasks, Time entries and Custom fields. On each card, you tell it which column in your file holds which field, for example which column holds the project name. If you upload several files, each card has a **tab** per file. Columns that point to another record (a project, task, client or person) also have a **Match … by** setting, which says how to find that record: by name, email or external ID.
3. **Preview.** Click **Check against workspace** to compare your files with what's already in Toggl. For each type of data it shows what will be created, what will be matched to existing records, and any warnings. Nothing is imported yet. If you change your files after this, you'll need to redo the mapping and run the check again. Then you choose a **mode** and click Import. The mode decides what happens when your file mentions something that isn't in Toggl yet, such as a project, task, client or person: 
     - **Create missing projects, tasks & clients** ("Create missing" in this guide): anything that isn't found is created, and unknown emails are added as new members. Use it to set up a new workspace.
     - **Match existing only:** no new clients, projects, tasks or members are created (missing tags still are). A row that names a client, project, task or member that doesn't exist is skipped, along with any time entries that depend on it. Blank cells are fine: an entry with no project or task imports without one. Matched projects and tasks can still be updated. Use it to add to, or update, a structure you've already built.

The import then runs in the background. When it finishes, an **error report** lists any rows that failed or were flagged, with the reason for each.

### Before you start

- **Who can use it:** workspace admins. To add *new* members, you also need to be an organization admin. Otherwise every new-member invitation is rejected.
- **Where it is:** **Admin settings → Connections → Data import → Universal CSV Importer**, then click **Open importer**.\[Image 02: Area.gif, the GIF showing where the importer is. Use the one you made for the Notion guide.\]
- ![Area-2](https://docs.toggl.com/hs-fs/hubfs/Area-2.gif?width=670&height=457&name=Area-2.gif)
- **What you can import:** you map columns onto five types of data: Members, Projects, Tasks, Time entries and Custom fields. 
    - **Clients** are a column on the Projects file, not a file of their own.
    - **Tags** are a column on the Projects, Tasks or Time entries file.
    - **Billable rates** can be imported on Projects and Members; labour costs only on Members.
    - **Custom field values** appear as extra fields on Projects and Tasks once the workspace has those custom fields. Custom fields require a Premium or Enterprise subscription; Starter doesn't include them.
    - **Custom field definitions** (the fields themselves) can be imported on the Custom fields card. That file can't contain anything else. The card only appears if your plan includes custom fields and your role lets you create them, so you can use it to set up your first ones.
- **Limits:** up to 100 MB per file, 300 MB per upload and 10 files per import. Only one import can run per workspace at a time; if one is already running, resume or cancel it first. Use UTF-8 or Windows-1252. UTF-16 isn't supported.

### Step 1: Prepare your file

Example files are illustrated below, with sample files to help you get started. Here are the full steps:

1. **One header row, one record per row.** Headers are used for auto-mapping, so plain names like `Client`, `Project`, `Task`, `Email`, `Date` and `Duration` map best.
2. **Split your data across files in whatever way suits it.** You can upload up to 10 files, and each file can feed several cards. For example, one timesheet export with project, client and task columns can create projects and tasks as well as time entries. 
     - **Large data:** split it into several files of the same type, such as one time entries file per year, to stay under 100 MB per file.
     - **Several sources:** upload the exports from each tool you're moving from in the same import. Each file gets its own mapping.
     - Files can refer to the same projects and tasks; those are only created once. But time entries are never merged, so a time entry that appears in two files is imported twice.
     - **First import?** Keeping each type of data in its own files (members, projects with a client column, tasks, and time entries) makes the mapping and the preview easier to check.
3. **Decide how each column should match, and spell names exactly as they are in Toggl.** Columns that point to another record have a **Match … by** setting in step 3, such as Match project by or Match assignee by. Most offer **Name** or **External ID**, and people columns (users, assignees) offer **Email** or **Name**. For the owner of a time entry, use Email (see item 8). When matching by name: 
     - Clients and projects match on the exact name, including capitals and punctuation, so `Acme` and `ACME` are two clients. A project only matches when its client matches too.
     - **Two projects with the same name?** If they have different clients, map a Client column so the importer picks the exact project-and-client pair. If they share the same client, or neither has a client, the importer treats the match as ambiguous and doesn't guess: those time entries aren't imported at all, not even without a project. Rename or merge the duplicate projects in Toggl before you import.
     - Tasks match without case sensitivity, within the same project and parent task. Only spaces before and after the name are ignored: a double space inside a name still counts, so `Logo concepts` won't match `Logo concepts`. Archived tasks are never matched; a new task is created instead.
     - For people, email is the more reliable choice, because two people can share a name. Task assignees can be matched by name if they're already in the workspace. An assignee who doesn't match anyone is left off the task, with a warning, and isn't invited. The task itself is still created, but its row shows as `has_errors` in the error report, so it can look as if the task failed.
4. **Subtasks go one level deep.** Fill in the **Parent task** column with the parent's name. The parent is matched, or created from that column if it doesn't exist. It doesn't need its own row, unless you refer to it by external ID. 
     - **Logging time to a subtask:** put the subtask's name in the time entry's Task column and import the tasks file in the **same import**, with **Parent task** mapped on the Tasks card. The entry then lands on the subtask. Spell task names exactly as in the tasks file: in **Create missing** mode, a name that doesn't match becomes a new top-level task (see step 3).
5. **Time entries need a start date, plus a duration or a start and end.** Map a Start column. If there's no Start column, map a task instead, and each entry takes its task's start date. Otherwise a task is optional: entries without one import as taskless entries, linked to the project if a project column is mapped.
6. **Use one date format within each column:** `YYYY-MM-DD`, `MM/DD/YYYY`, `DD/MM/YYYY`, `DD.MM.YYYY` or `YYYY/MM/DD`. Durations can be decimal hours (2.5), HH:MM:SS or minutes. Negative durations are rejected.
7. **Include a timezone in your timestamps.** A time with no timezone offset is imported as UTC, so `2026-10-01 09:00` becomes 09:00 UTC. To keep your local time, export times with an offset, such as `2026-10-01T09:00:00+02:00`. If a row has only a date, it gets the **Default time** you set when mapping the Start column (09:00 UTC unless you change it). 
     - **Dates with a time:** write them as `2026-04-03 10:00` (year-month-day). For now, when a date also has a time, the date format you pick in step 3 is ignored and the date is read as month/day: in a day/month file, `03/04/2026 10:00` becomes 4 March, not 3 April. `03.04.2026 10:00` and `2026/04/03 10:00` are rejected.
8. **Decide who should own the time entries.** 
     - **Each entry under the right person:** include a user column with each person's Toggl login email, and match it by **Email** in step 3. Matching by name doesn't currently work for time entries: the import stops at **Mapping incomplete**. If a row's user cell is empty, that one entry goes to the person running the import.
     - **Everything under the person running the import:** don't map a user column at all (see "Mapping incomplete" under Common hurdles).
     - The user column sets who owns each entry; it doesn't assign anyone to tasks.
     - If an email doesn't match anyone in the workspace, **Create missing** mode **adds them as a new member**, whether or not Send invitation emails is ticked, so check emails and the preview carefully. Match existing only skips the row. Exception: if the person is already in your organization but not in this workspace, they aren't added, and their time entries fail.
9. **Keep time entry descriptions to 400 characters or fewer.** Longer ones fail rather than get cut.
10. **Several values in one cell** (tags, several task assignees, project members): use semicolons between values, like `research;strategy`.

### Example files

Here's a small set of files that follows every rule above: one for projects, one for tasks with subtasks, and one for time entries. The numbered notes match the highlighted cells.

**Projects file**

![03\_example\_projects\_file](https://docs.toggl.com/hs-fs/hubfs/03_example_projects_file.png?width=670&height=163&name=03_example_projects_file.png)

**Tasks file with subtasks**

![04\_example\_tasks\_file](https://docs.toggl.com/hs-fs/hubfs/04_example_tasks_file.png?width=670&height=227&name=04_example_tasks_file.png)

**Time entries file**

![05\_example\_time\_entries\_file](https://docs.toggl.com/hs-fs/hubfs/05_example_time_entries_file.png?width=670&height=251&name=05_example_time_entries_file.png)

Download the sample files to use as a starting point: [projects.csv](https://docs.toggl.com/hubfs/projects.csv?hsLang=en), [tasks.csv](https://docs.toggl.com/hubfs/tasks.csv?hsLang=en), [time\_entries.csv](https://docs.toggl.com/hubfs/time_entries.csv?hsLang=en).

### Step 2: Upload

Drag in your files, or click to choose them.

### Step 3: Map columns

1. If you've imported from the same source before, apply a saved **mapping profile** first. It fills in the mapping, which you can still check and change. Then map each column onto the right card: Members, Projects, Tasks, Time entries or Custom fields.  
   ![06\_time\_entries\_mapping](https://docs.toggl.com/hs-fs/hubfs/06_time_entries_mapping.png?width=670&height=579&name=06_time_entries_mapping.png)
2. Check the auto-detected matches. Map every required field (marked) first, then optional ones under **More properties**. 
     - Not every column is matched automatically, even with the exact name. **Parent task**, **Assignees** and project **Status** have to be picked by hand. A **Tag** or **Tags** column maps automatically when the file clearly holds projects or tasks; otherwise pick it by hand.
     - Uploaded several files? Each card has a tab per file. Check the right file's tab.
     - Mapping a Task column on time entries also sets up tasks from that column (you'll see it as a tab on the Tasks card). In **Create missing** mode, a task name in your time entries file that doesn't match a task in your tasks file or the workspace becomes a **new top-level task**. "Use this column to match existing records" stops duplicates, but not new tasks. Only **Match existing only** prevents new tasks. You can spot this in step 4.
     - On the Time entries card, check **Match member by**. A column headed `User` or `Member` is matched by Name, which doesn't currently work for time entries and keeps Next greyed out. A column headed `Email`, `User email` or `Member email` is matched by Email. If your column holds emails, make sure it's set to **Email**.
3. Set the date and duration format so they match your file.
4. If **Next** is greyed out, read the message next to it. Usually a required field is unmapped (on time entries, often the Start column), or a column was guessed as the wrong type. For example, a Start date column in a projects file can be read as a time entry field.
5. Check which card each column is mapped to before you continue.

You can still add, remove or swap files after you run **Check against workspace**, but then the mapping has to be redone and the check run again. In the importer, removing a file at that point asks you to **Start over**, which discards the mapping. Nothing has been imported yet. Files are only locked once you click **Import**.

![07\_start\_over](https://docs.toggl.com/hs-fs/hubfs/07_start_over.gif?width=670&height=592&name=07_start_over.gif)

### Step 4: Review and import

![08\_check\_against\_workspace](https://docs.toggl.com/hs-fs/hubfs/08_check_against_workspace.png?width=670&height=579&name=08_check_against_workspace.png)

1. Click **Check against workspace** to preview what will be created and what will be matched to existing records. 
     - Imported tasks? The Tasks count should match the number of rows in your tasks file. If it's higher, some task names in your time entries file don't match, and they'll become new top-level tasks. Click **Start over**, fix the names and upload the file again.
     - If you'll import from the same source again, save the mapping as a profile here.
2. Choose a mode (see **How the import works** at the top): 
     - **Create missing projects, tasks & clients:** creates anything that isn't in Toggl yet. For setting up a new structure.
     - **Match existing only:** creates no new clients, projects, tasks or members (missing tags are still created), and skips rows that name something that doesn't exist. For adding to a structure you've already built. A matched record is updated instead of duplicated.
3. **Importing new members?** In **Create missing** mode, tick **Send invitation emails**. It's off by default. Members are added either way, but they only get an email if it's ticked.
4. Click **Import**. It keeps running in the background, so you can leave the page.
5. When it finishes, check the **Error report** block. If any rows failed or were flagged, click **Download** for that file. You'll get a CSV of the rows that had a problem, with a **Result** column. Rows that imported without problems aren't in the report. 
     - `has_errors`: the row had a problem, but may have partly imported.
     - `skipped`: the row wasn't imported.
     - `imported_with_warnings`: the row **did** import, with a warning.
     - `not_reported`: never on a row. It only appears on one extra line at the very end of the report, with general notes that aren't tied to a row.
6. **Clean up the file before you re-import it.** Re-uploading it as it is duplicates time entries. 
     - Delete every row whose Result is `imported_with_warnings`, because those already imported.
     - For `has_errors` rows, check whether the time entry already exists in Toggl, and delete the row if it does.
     - Fix the remaining rows, then import the file.

![09\_import\_result](https://docs.toggl.com/hs-fs/hubfs/09_import_result.png?width=670&height=579&name=09_import_result.png)

**Don't re-upload a file that partly worked.** Clients, projects and tasks that match by name are reused, but time entries are not: uploading the same timesheet again creates a second copy of every entry.

### More things you can do

- **Update existing members.** A Members file can set access level (admin, manager, member, time tracker or guest), active or inactive status, and working hours. Re-importing it can change existing members' roles. The roles of organization admins and your own account are never changed. Other details can be: an organization admin (other than the owner) can be set to inactive, and working hours and rates are changed for everyone in the file, including organization admins and you.
- **Add project managers.** A Managers column on Projects makes those people managers of the project. It only adds managers; it never removes an existing one.
- **Create projects from a template.** Add a Template column with the template's exact name. It only applies when the import creates a new project, which needs **Create missing** mode. 
    - The template's tasks, billable rates and labour costs, milestones, alerts, members and access, fixed fee, attachments and tags are copied in. Your plan or permissions can still drop some of these, with a warning.
    - A filled cell in your row replaces the template's value for name, client, description, billable, private, color, start, end, estimate, fixed fee, status and custom fields. A blank cell keeps the template's value, including its client. Templates don't carry dates.
    - Tags, tasks or members from other columns are added on top of the template's.
    - If a project with that name already exists, or you use Match existing only, the template isn't applied. The name must match exactly one visible, unarchived template; otherwise the row is skipped.
- **Set more project details.** Projects also accept status (active, archived or completed), fixed fee with currency, estimate, private, and color.
- **Match by external ID.** Map an External ID column on the Projects or Tasks card, or set **Match … by** to External ID on a Client, Project, Task or Parent task column. External IDs link rows within the same import, such as a time entry to a project in the same upload. In a later import, existing clients, projects and tasks are matched by name, so keep names the same as in Toggl, or rename them in the CSV first. 
    - Time entries have no external ID, so importing the same time entries again creates duplicates.
- **Check Import history.** Data import lists past imports and their status, and you can download the original files again from there. To see who started a CSV import, check the history inside the Universal CSV Importer, which shows "by you" or the person's name.

![10\_import\_history](https://docs.toggl.com/hs-fs/hubfs/10_import_history.png?width=670&height=579&name=10_import_history.png)

### Common hurdles and how to fix them

#### Rows failed or were skipped, and it's unclear why

Download the error report from the Error report block at the end of the import. It gives the reason for each row. Common causes:

- A required field left unmapped, often the Start column on time entries.
- A date format that doesn't match the file.
- A time entry description over 400 characters.
- New members invited by a workspace admin who isn't an organization admin.

#### Duplicate time entries after a second upload

Time entries are never matched to existing ones. Never re-import the original file. If you re-import the error report, first delete the `imported_with_warnings` rows and any `has_errors` rows whose entries already exist. If you've already doubled up, delete the extra entries in Toggl.

#### A project came in twice as "Name (csv)"

In **Create missing** mode, a project that already belongs to another import, including a Track import or sync, isn't reused, so a second project is created. Use **Match existing only** when adding to projects that already exist. Also check that the client name matches exactly, because a project only matches when its client does. (Clients themselves, including ones from Track, are now reused when the name matches exactly.)

#### Every time entry became its own project or task

The project or task column was probably mapped to the wrong field, or names don't match the existing ones. Re-check the mapping and use **Match existing only**.

#### Time entries failed with "project … not found", but the project exists

Two projects in Toggl probably share the same name and the same client (or neither has a client), so the importer can't tell which one you mean. In the error report:

- The project row is `skipped`, with a warning that more than one existing project has this name under the same client.
- Each of its time entry rows has failed with `project "X" not found`. In Match existing only mode, they show as `skipped` instead.

None of those entries were imported. Rename or merge the duplicate projects in Toggl, then import only those rows again. If the two projects have different clients, map a Client column instead (see step 1, item 3).

#### "Mapping incomplete: time\_entries.csv: Member needs Email address"

The Members card also picks up the user column from your time entries file, and a member can't be added without an email. Open the **Members** card, go to the time entries file's tab, and map your email column to **Email**. If your file has no email column, you have two options:

- **Keep each entry under the right person:** add a column with each person's Toggl login email, upload the file again, and set Match member by to **Email** on the Time entries card.
- **Put every entry under you:** do these in order, because the Members fields are mapped again automatically while the Member column is still mapped: 
    1. On the **Time entries** card, unmap the Member column.
    2. On the **Members** card, open the time entries file's tab and clear every mapped field one by one (click the selected column in each picker).
   Check the preview before you import.

Matching people by name alone doesn't currently work for time entries.

#### Members didn't get an invitation email

Tick **Send invitation emails** before importing (it's off by default). It only controls whether the email is sent: members are added either way, with their names if you mapped a name column. If you already imported without it, send invites from **Members**.

#### People show on time entries but not as task assignees

The user column on time entries only sets who owns each entry. To assign people to tasks, map an assignee column on the Tasks card.

#### I want to assign teams

Add a **Teams** column:

- On a **project**, it shares the project with that team. A team that doesn't exist yet is only created in **Create missing** mode, and only if you're allowed to manage organization teams. Otherwise it isn't created.
- On a **task**, it adds an existing team, matched by name without case sensitivity. It won't create a new team from a task row.
- It only adds teams; it never removes anyone. Creating projects from a template whose tasks already have teams is another option.

#### I want to update existing projects or tasks in bulk

Use **Match existing only**. A matched project or task is updated wherever a mapped cell differs, including custom field values in the file. Blank cells and unmapped fields are left alone. Clients matched by name aren't changed.

#### Recurring work

- Tasks have a recurring column: daily, every weekday, weekly, biweekly, monthly by date, monthly by weekday, yearly, or a custom rule in RRULE format (RFC 5545).
- The importer doesn't create recurring projects. Set those up in Toggl first.
- Time entries for an existing recurring project land on the right period, but any project values in the row aren't applied.

#### Non-admins can't import

Only workspace admins can run the importer. Team members' time has to go through an admin.

- [Getting started](https://docs.toggl.com/getting-started?hsLang=en#main-content)

    - [Start here](https://docs.toggl.com/getting-started?hsLang=en#start-here)
    - [Quickstart Guides](https://docs.toggl.com/getting-started?hsLang=en#quickstart-guides)
    - [Set up your Workspace](https://docs.toggl.com/getting-started?hsLang=en#set-up-your-workspace)
    - [Toggl Guides for Toggl Track and Toggl Plan users](https://docs.toggl.com/getting-started?hsLang=en#toggl-guides-for-toggl-track-and-toggl-plan-users)
- [Using Toggl, page by page](https://docs.toggl.com/using-toggl-page-by-page?hsLang=en#main-content)

    - [Timer](https://docs.toggl.com/using-toggl-page-by-page?hsLang=en#timer)
    - [Reports](https://docs.toggl.com/using-toggl-page-by-page?hsLang=en#reports)
    - [Projects](https://docs.toggl.com/using-toggl-page-by-page?hsLang=en#projects)
    - [Tasks](https://docs.toggl.com/using-toggl-page-by-page?hsLang=en#tasks)
    - [Timeline](https://docs.toggl.com/using-toggl-page-by-page?hsLang=en#timeline)
    - [Members](https://docs.toggl.com/using-toggl-page-by-page?hsLang=en#members)
    - [Approvals](https://docs.toggl.com/using-toggl-page-by-page?hsLang=en#approvals)
    - [Time Off](https://docs.toggl.com/using-toggl-page-by-page?hsLang=en#time-off)
    - [Profile Settings](https://docs.toggl.com/using-toggl-page-by-page?hsLang=en#profile-settings)
- [Admin Settings & Security](https://docs.toggl.com/admin-settings-security?hsLang=en#main-content)

    - [Workspace Setup](https://docs.toggl.com/admin-settings-security?hsLang=en#workspace-setup)
    - [Tracking & Rates](https://docs.toggl.com/admin-settings-security?hsLang=en#tracking-rates)
    - [Single Sign-on](https://docs.toggl.com/admin-settings-security?hsLang=en#single-sign-on)
- [Subscription & Billing](https://docs.toggl.com/subscription-billing?hsLang=en)
- [Toggl Apps](https://docs.toggl.com/toggl-apps?hsLang=en#main-content)

    - [Mobile](https://docs.toggl.com/toggl-apps?hsLang=en#mobile)
    - [Desktop](https://docs.toggl.com/toggl-apps?hsLang=en#desktop)
    - [Browser Extension](https://docs.toggl.com/toggl-apps?hsLang=en#browser-extension)
- [API & Integrations](https://docs.toggl.com/api-integrations?hsLang=en)
- [Troubleshooting & FAQs](https://docs.toggl.com/troubleshooting-faqs?hsLang=en#main-content)

    - [My Toggl Acount](https://docs.toggl.com/troubleshooting-faqs?hsLang=en#my-toggl-acount)
    - [Common Questions](https://docs.toggl.com/troubleshooting-faqs?hsLang=en#common-questions)

<https://github.com/toggl/> <https://www.linkedin.com/company/toggl/> <https://community.toggl.com/>

Copyright © 2026. Toggl. [Privacy Policy](https://toggl.com/legal/focus/privacy/)