<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ai - Bagaag</title>
	<atom:link href="https://www.bagaag.com/tag/ai/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.bagaag.com</link>
	<description></description>
	<lastBuildDate>Mon, 20 Oct 2025 02:33:18 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://www.bagaag.com/wp-content/uploads/2025/09/cropped-bagaag-favicon-32x32.png</url>
	<title>ai - Bagaag</title>
	<link>https://www.bagaag.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
					<title>Todoist Actual Backup</title>
					<link>https://www.bagaag.com/todoist-actual-backup/</link>
					<comments>https://www.bagaag.com/todoist-actual-backup/#respond</comments>
		
		<dc:creator><![CDATA[matt]]></dc:creator>
		<pubDate>Sun, 19 Oct 2025 14:07:20 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[python]]></category>
		<guid isPermaLink="false">https://www.bagaag.com/?p=636</guid>

					<description><![CDATA[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. 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. [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>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. </p>



<ul class="wp-block-list">
<li><a href="https://www.bagaag.com/todoist-actual-backup/#why">Part 1</a>: Why did I need to build this?</li>



<li><a href="https://www.bagaag.com/todoist-actual-backup/#ai" data-type="internal" data-id="#ai">Part 2</a>: Using AI to write software</li>



<li><a href="https://www.bagaag.com/todoist-actual-backup/#script">Part 3</a>: The end product with source code and instructions for use</li>
</ul>



<span id="more-636"></span>



<h4 class="wp-block-heading" id="why">Why did I need to build this? </h4>



<p><a href="https://www.todoist.com/">Todoist</a> 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.</p>



<p>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.</p>



<p>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.</p>



<p>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 <a href="https://en.wikipedia.org/wiki/Revenue_stream">MRR</a>. 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.</p>



<p>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.</p>



<p>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.</p>



<h4 class="wp-block-heading" id="ai">Using AI to Write Software</h4>



<figure class="wp-block-image size-full is-resized"><img fetchpriority="high" decoding="async" width="924" height="660" src="https://www.bagaag.com/wp-content/uploads/2025/10/and-now-for-something-completely-different.png" alt class="wp-image-652" style="width:400px" srcset="https://www.bagaag.com/wp-content/uploads/2025/10/and-now-for-something-completely-different.png 924w, https://www.bagaag.com/wp-content/uploads/2025/10/and-now-for-something-completely-different-300x214.png 300w, https://www.bagaag.com/wp-content/uploads/2025/10/and-now-for-something-completely-different-768x549.png 768w" sizes="(max-width: 924px) 100vw, 924px"></figure>



<p>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 <em>substantially</em> longer to do so? Yes.</p>



<p>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.</p>



<p>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.</p>



<p>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. <a href="https://en.wikipedia.org/wiki/Large_language_model">LLMs</a> are <em>really</em> 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.</p>



<p>I started with this basic prompt:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.75rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#39404f;color:#c8d0e0">Markdown</span><span role="button" tabindex="0" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>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.</textarea></pre><svg style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" /><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" /></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #D8DEE9FF">Create a command line script that exports all active and completed tasks from the Todoist API to a JSON file.</span></span>
<span class="line"><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> Use the Todoist API documented at https://developer.todoist.com/api/v1/</span></span>
<span class="line"><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> Use the Todoist python client documented at https://doist.github.io/todoist-api-python/</span></span>
<span class="line"><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> Use a .venv to scope the python environment.</span></span>
<span class="line"><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> Include tasks from all active and archived projects.</span></span>
<span class="line"><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> Include all active and completed tasks available in the API. This may require paging through results.</span></span>
<span class="line"><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> Save attachments to an ./attachments folder and reference the attachment name for each in the JSON output.</span></span>
<span class="line"><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> Tasks are nested under project objects in the JSON result, with each project object containing all available project fields in the API.</span></span>
<span class="line"><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> Include all task fields available in the API for each task.</span></span>
<span class="line"><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> If a task has comments, nest them in an array under the task in the JSON output.</span></span>
<span class="line"><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> 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. </span></span>
<span class="line"><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> Use the API key provided in the TODOIST_KEY environment variable.</span></span>
<span class="line"><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> 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.</span></span>
<span class="line"><span style="color: #ECEFF4">-</span><span style="color: #D8DEE9FF"> 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.</span></span></code></pre></div>



<p>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.</p>



<p>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.</p>



<p>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.</p>



<p>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. </p>



<p>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.</p>



<p>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.</p>



<p>Sadly, developers today are helping to train AI to do this job with full autonomy. Tools like Google’s <a href="https://jules.google/">Jules</a> 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?</p>



<p>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.</p>



<h4 class="wp-block-heading" id="script">The Todoist Actual Backup script</h4>



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



<p><strong>Todoist Actual Backup</strong></p>



<p>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.</p>



<p><strong>Features</strong></p>



<ul class="wp-block-list">
<li>Exports all active and completed tasks from all projects (active and archived)</li>



<li>Nests tasks under their respective projects, including all available fields</li>



<li>Includes comments for each task</li>



<li>Downloads attachments to <code>output/attachments/</code> and references them in the JSON and HTML output</li>



<li>JSON and HTML files are named with the current date when the script is run</li>



<li>Maintains <code>Todoist-Completed-History.json</code> so completed tasks older than Todoist’s 90-day API window stay in future exports</li>



<li>Reuses archived comments for completed tasks to avoid unnecessary API calls (assumes no new comments after completion)<a href="https://git.bagaag.com/matt/Todoist-Actual-Backup/#setup"></a></li>
</ul>



<p><strong>Setup</strong></p>



<ul class="wp-block-list">
<li>Ensure you have Python 3.8 or newer installed. Check with <code>python --version</code> on the command line.</li>



<li>The script uses a <code>.venv</code> for dependencies. Run the following in a terminal from an empty project folder: <br><code>python -m venv .venv </code><br><code>source .venv/bin/activate </code><br><code>pip install -r requirements.txt</code></li>



<li>Get your API key from <a href="https://app.todoist.com/app/settings/integrations/developer">Todoist</a></li>



<li>Optionally set your Todoist API key in the <code>TODOIST_KEY</code> environment variable. If the environment variable is not set, the script will prompt for it.<a href="https://git.bagaag.com/matt/Todoist-Actual-Backup/#usage"></a></li>
</ul>



<p><strong>Usage</strong></p>



<ol class="wp-block-list">
<li>In a terminal, run <code>source .venv/bin/activate</code> if needed to enter the virtual environment.</li>



<li>Run the script with the <code>export</code> argument: <code>python export_todoist.py export</code></li>
</ol>



<p>This will create <code>output/Todoist-Actual-Backup-YYYY-MM-DD.json</code> and <code>output/Todoist-Actual-Backup-YYYY-MM-DD.html</code>, and it will update <code>output/attachments/</code> with any downloaded files while leaving <code>Todoist-Completed-History.json</code> in the project root. Keep <code>Todoist-Completed-History.json</code> 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.</p>



<hr class="wp-block-separator has-alpha-channel-opacity">



<p>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.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.bagaag.com/todoist-actual-backup/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
