Todoist Actual Backup

I built a Python script to export data from a Todoist account. There are a few topics here: SaaS customer data access, using AI for development, and the app itself.

  • Part 1: Why did I need to build this?
  • Part 2: Using AI to write software
  • Part 3: The end product with source code and instructions for use

Why did I need to build this?

Todoist is a mobile and web-based task manager app. As a maker of lists, I love this service. A desktop web interface and snappy mobile app stay out of the way and assist my mind in the way only great software can. They’ve gracefully combined all the features you could want in a task list app without making it overly complicated to use. Hats off for that.

As much as I love Todoist, it’s a “software as a service” or cloud app. That means they store and control my data, ultimately deciding what data to retain and for how long, what data to make available and how. It’s a reasonable trade-off for a great app with seamless synchronization between desktop and mobile environments. That said, I pour considerable amounts of personal data that is valuable to me into this app. My access to that data outside the confines of a paid service is equally important to me.

Todoist provides a nice backup page where you can download daily zips of your data. Unfortunately, those backups lack much of the data you’ve put into the app. They do not include completed tasks, related sub-tasks, comments or attachments. The backup provided is a zip of CSV files, not easily viewable outside of a spreadsheet. That’s a lot of data and access they’re choosing not to make available to paying customers.

The motivation behind that decision is obvious. Many SaaS businesses are willing to shackle customer data in order to subtly force reliance on the monthly subscription. Making it a little bit harder to leave the service helps maintain the all-important MRR. Todoist is not alone in this. As subscription software services have become the norm, so has vendor lock-in via data access limitations. Customers who aren’t technically savvy or interested enough to host their own open source software apps are stuck with little recourse.

For some reason, all that missing data in Todoist backups is accessible if you’re a developer. Kudos to them for providing a nice stable API with — as far as I can tell — complete data access. But shame on them for significantly limiting the data paying customers can export unless they have software development skills. Come on, guys.

Faced with this conundrum, I decided to build a solution for exporting all the data from Todoist using their API, so customers without development skills can back up and access their own data outside the paid service.

Using AI to Write Software

And now for something completely different. The fear and loathing and unchecked optimism around AI in the software development community is rampant right now. As a developer wanting to remain relevant in this economy, it is a technology I need to be using. Would I have enjoyed writing this script on my own? Yes. Would it have taken substantially longer to do so? Yes.

A popular expression that describes what’s going on with AI and software development right now is that “English is the new programming language”. Software developers have traditionally spent their day writing code in some programming language like C# or JavaScript or Python. AI now does the programming well enough that it’s more efficient if developers become orchestrators, specification writers and code reviewers, telling the AI what to do and monitoring the results in a feedback loop until the job is done to satisfaction.

On one hand, I’m blown away by this technology. It’s really good. I love being able to whip up a quick shell script in the time it takes to describe what it should do. On the other hand I see the imminent death of software development as we know it. I love programming, so that’s sad.

After learning Todoist has a robust API that provides access to the data I wanted to backup, I decided to use Github Copilot to build it for me in Python. LLMs are really good at language, and programming is just another language to them. Copilot is implemented as a chat bot that sits in the IDE, the software developers use to write, build, run and test code. I used Visual Studio Code for this project.

I started with this basic prompt:

Markdown
Create a command line script that exports all active and completed tasks from the Todoist API to a JSON file.
- Use the Todoist API documented at https://developer.todoist.com/api/v1/
- Use the Todoist python client documented at https://doist.github.io/todoist-api-python/
- Use a .venv to scope the python environment.
- Include tasks from all active and archived projects.
- Include all active and completed tasks available in the API. This may require paging through results.
- Save attachments to an ./attachments folder and reference the attachment name for each in the JSON output.
- Tasks are nested under project objects in the JSON result, with each project object containing all available project fields in the API.
- Include all task fields available in the API for each task.
- If a task has comments, nest them in an array under the task in the JSON output.
- Name the JSON file Todoist-Actual-Backup-YYYY-MM-DD.json where YYYY-MM-DD is the current date. Overwrite the file if it already exists. 
- Use the API key provided in the TODOIST_KEY environment variable.
- Run the export if the script is run with the "export" argument. Any other argument, or no argument, displays a description of what the script does and how to use it.
- After writing the JSON result, use its contents to create an attractive human readable version in a single file HTML document named Todoist-Actual-Backup-YYYY-MM-DD.html. This file groups tasks by project and includes all project and task details, including attachments (link to the locally downloaded files) and comments. Use a Jinja2 template to generate the HTML file.

From this prompt and after waiting a minute or two, I got a finished, but not working Python script. That AI produced what it did is impressive, but it’s still a lot like working with a junior developer who knows every programming language. It doesn’t make the best decisions if not provided with sufficient direction.

After several cycles of run, paste the error into chat, repeat, I had a working script that met the requirements I’d given. That initial process didn’t take more than a few hours.

I then tested and tweaked, adding several features along the way. For example, I realized I was only getting 90 days of completed tasks and that was a limitation imposed by Todoist. So I asked it to maintain a local file of completed tasks across runs to extend their storage beyond 90 days and include them in exports.

Adding features like this works a lot like the initial prompt. Just explain what the feature should do, and maybe provide some technical direction where needed. The AI comes back with detailed explanations of the logic its going through in planning and executing a solution, all in plain English. Source code control allows the developer to clearly see and review the changes the AI has made at each step.

Creating this script felt collaborative. In addition to generating and updating code, Copilot is great for discussing feature implementation options and providing recommendations that are backed with solid reasoning. For example, when I ran into API rate limits during testing, we had a good discussion over the pros and cons of caching API calls vs a more complex rate limiting mechanism. During that, I realized the issue was less that we needed to limit the rate and more that the initial implementation was highly inefficient in its use of the API. That led to a huge decrease in the number of API calls required to run the script.

Even though this is programming in English, someone who doesn’t understand software development would probably struggle with anything even marginally complicated. If you’re not careful, you can easily end up with software that seems to work but is a real mess under the hood. How much does that matter if AI is doing the coding? It’s a good question.

Sadly, developers today are helping to train AI to do this job with full autonomy. Tools like Google’s Jules can be assigned tasks via a ticketing system, which are then completed, tested and submitted for review by AI. As this becomes the norm, a team of 5 software developers and a team lead could easily be reduced to just the team lead. And how strong of a developer will that team lead be when they haven’t written their own code in years?

In any case, I spent maybe 8 hours total on this script. My direct code edits were limited to small visual changes to the HTML output that weren’t worth bothering with the AI to make. It would have taken me substantially longer to write this from scratch. So it’s a win for AI. I enjoyed working with Copilot to build it — it’s literally like assigning tasks to a developer and reviewing the results in a feedback loop.

The Todoist Actual Backup script

OK, enough with the blah bidy blah. You can grab the code at https://git.bagaag.com/matt/Todoist-Actual-Backup/. Here’s the README for the project:

Todoist Actual Backup

Todoist is a SaaS task manager. Todoist provides backups of current tasks, but they do not include completed tasks, subtask relationships, comments or attachments. Nor does it provide a human-readable backup in HTML. This Python script provides a command-line tool to export all available active and completed tasks from the Todoist API to a JSON file, including attachments, subtasks and comments, and generates a human-readable HTML backup.

Features

  • Exports all active and completed tasks from all projects (active and archived)
  • Nests tasks under their respective projects, including all available fields
  • Includes comments for each task
  • Downloads attachments to output/attachments/ and references them in the JSON and HTML output
  • JSON and HTML files are named with the current date when the script is run
  • Maintains Todoist-Completed-History.json so completed tasks older than Todoist’s 90-day API window stay in future exports
  • Reuses archived comments for completed tasks to avoid unnecessary API calls (assumes no new comments after completion)

Setup

  • Ensure you have Python 3.8 or newer installed. Check with python --version on the command line.
  • The script uses a .venv for dependencies. Run the following in a terminal from an empty project folder:
    python -m venv .venv
    source .venv/bin/activate
    pip install -r requirements.txt
  • Get your API key from Todoist
  • Optionally set your Todoist API key in the TODOIST_KEY environment variable. If the environment variable is not set, the script will prompt for it.

Usage

  1. In a terminal, run source .venv/bin/activate if needed to enter the virtual environment.
  2. Run the script with the export argument: python export_todoist.py export

This will create output/Todoist-Actual-Backup-YYYY-MM-DD.json and output/Todoist-Actual-Backup-YYYY-MM-DD.html, and it will update output/attachments/ with any downloaded files while leaving Todoist-Completed-History.json in the project root. Keep Todoist-Completed-History.json somewhere safe (e.g., in source control or a backup location); it is the only way the exporter can retain completed tasks older than Todoist’s 90-day API retention window.


I’ve incorporated this script into my weekly backup process. It was fun to build and hopefully it will be useful to others. I may explore providing a stand-alone executable for it so folks who aren’t comfortable installing Python can use it. Let me know in the comments if that would be useful to you.

Instagram

Leave a Reply

Your email address will not be published. Required fields are marked *

To respond on your own website, enter the URL of your response which should contain a link to this post's permalink URL. Your response will then appear (possibly after moderation) on this page. Want to update or remove your response? Update or delete your post and re-enter your post's URL again. (Find out more about Webmentions.)