Use Loop Habit Tracker + Notion API for Progress Logging

Setting up loop habit tracker for real use

The Loop Habit Tracker app looks simple enough but the first time I tried to pair it with Notion for progress tracking I got stuck before even opening the API docs. There is no built in Notion integration so you have to basically make them talk to each other with third party glue like Zapier or a custom script. I thought I could do it in fifteen minutes. I lost most of my afternoon instead.

The key is that Loop doesn’t push data anywhere automatically. It just sits there recording your streaks locally. So the way I got mine to send updates was by exporting data from Loop as a CSV file via its settings menu. That CSV isn’t pretty. The timestamps are in an odd format and the habit names sometimes have spaces at the end for no reason. That meant my Notion database rejected them at first until I stripped whitespace. Little things like that will drive you nuts.

Table showing field mismatch example:
Habit Name | CSV Column Header | Notion Property Name
———–|——————|———————
Morning run| habit | Habit Name
Drink water| habit | Habit Name

If you are thinking you can just attach the CSV to your Notion page, sure you can — but it won’t be searchable or sortable. You want it in proper Notion properties. That’s where the API comes in. I ended up writing a small Python script that reads the Loop CSV and sends each row to Notion. At one point, the Notion API rejected half of my rows because I was sending a `null` value for streak length. Turns out you can’t send a blank for a Number field in Notion. You either send zero or skip that property. ¯\\_(ツ)_/¯

Creating a notion database for tracking

Before you even think about automating, you need a database in Notion that matches your habit data structure. I made columns for Habit Name, Date, Streak Count, and Notes. Also, I created a Relation property linking to another table I use for personal goals so I can see habit progress in one dashboard.

When you create this database, pay attention to the property types. If you make the Date field a plain text, you will have a miserable time later formatting dates from Loop. The Loop export CSV gives dates as `YYYY-MM-DD` already, so Notion can parse them if the property is set as a Date type. But here’s the kicker — Loop stores them as UTC midnight, so depending on your timezone, your “morning run” shows up as happening the day before. I fixed that by adding `timedelta(hours=offset)` in my script.

Example raw snippet from my console when I forgot that date issue:
“`
{‘object’: ‘error’, ‘status’: 400, ‘message’: ‘Invalid date format.’}
“`
That error popped for every single row until I realized it was timezone drift.

Connecting with the notion api

You can get your Notion integration key by going into your Notion workspace settings and creating a new internal integration. Then you share your tracking database with that integration. If you don’t share it, the API just acts like it doesn’t exist — no error, no warning, just no data.

To actually push habits from Loop into Notion, I used the official Notion API endpoint for creating a database page. Every request needs your secret key in the Authorization header and the database ID in the URL. I found the easiest way to get the database ID is by copying it from the browser URL when the database is open — it is that long string of characters in the middle, between slashes. If you get that wrong, your script might run but you will be staring at an empty database wondering why nothing appears.

Automating the data flow with python

Manually exporting the Loop data every day is as annoying as it sounds. I set up a scheduled task to run my script every morning at 6am. The script does the following:
1. Reads the latest export from Loop folder on my Android device (synced with Google Drive).
2. Parses each row.
3. Cleans trailing spaces from habit names.
4. Converts the weird streak data to integers.
5. Pushes each day’s habit entries to Notion.

When I tried to push all habits in bulk, Notion API rate limited me and I got 429 errors. The messages were polite but my data just wasn’t there. So I slowed it down by adding a one second `sleep()` between requests. Yes it’s slower, but at least I’m not missing records anymore 😛

Handling updates instead of duplicates

The first big problem you hit is duplication. If you run the script twice for the same date, you’ll have two identical habit entries in Notion. That’s especially annoying with streak tracking because you can’t tell which entry is original. To fix it, I added a check before creating a new page — my script queries the database for a matching Habit Name and Date. If found, it updates that page instead.

The Notion API lets you update existing pages if you know the page ID. I just pull the ID from the query result and then send a PATCH request. It’s slower than just dumping all records in but it keeps the database clean.

Making progress visible without opening notion

Storing data is nice, but I wanted my progress without opening Notion every time. So I added a step to my script that writes the completed habit count into a text file that sits in my desktop widgets area. The file output is simple — something like:
“`
Habits done today: 4/6
Streak longest: 22 days
“`
Now I get a quick glance view without toggling apps. Feels much lighter and keeps me from breaking focus.

Dealing with sudden breaking changes

Every so often an app update or API change just breaks the whole thing. One morning my script failed at authentication because Notion changed something with token expiration. The error log just said: `unauthorized`. I had to regenerate my integration key from the Notion settings and update my script. Another time, Loop changed their CSV export order. Suddenly my `date` variable was pulling the Habit Name instead. If I hadn’t noticed right away, my Notion logs would have shown a 2000 day streak for “12-05-2024”. Yikes.

The moral is, don’t set and forget these automations. They will break. Keep the script readable so you can fix it. Leave yourself comments. Future you will thank past you when you are staring at a wall of data that makes no sense 🙂

Things I would do differently next time

I’d probably cut the middleman and write a mobile shortcut that logs habits to Notion instantly when I check them off in Loop. Exporting and parsing feels like overkill now. Also I’d store a local backup in plain text every time I run the sync so I never lose history even if Notion wipes something. I still think the setup is worth it though — it’s strangely satisfying to have your habits live in a nice clean database that you can sort by month or streak length on a whim.

Leave a Comment