🚀 Executive Summary

TL;DR: The article presents a Python script to automate syncing Apple Notes from a designated folder to individual plain text files on a Mac. This solution leverages the notes-mac-cli tool to extract notes, cleans their content, and can be scheduled with cron to integrate Apple Notes into a portable, version-controlled knowledge base.

🎯 Key Takeaways

  • The `notes-mac-cli` Python package is essential for programmatically interacting with Apple Notes, as macOS lacks a native API for direct database access.
  • The core Python script fetches notes in JSON format, sanitizes titles for valid filenames, and strips HTML tags from note bodies using regular expressions for clean text output.
  • Automating the script with `cron` on macOS requires using absolute paths for both the Python executable and the script itself to overcome `cron`’s minimal environment limitations.

Syncing Apple Notes to Text Files on Mac via Scripting

Syncing Apple Notes to Text Files on Mac via Scripting

Hey there, Darian Vance here. As a DevOps engineer at TechResolve, my life revolves around automation. If I have to do something more than twice, I script it. For a long time, my workflow for capturing quick thoughts was broken. I’d jot down an idea in Apple Notes on my phone, then later, I’d manually copy-paste it into my main knowledge base, which is just a folder of text files I keep in Git. I finally tracked the time and realized I was wasting a couple of hours a month on this mindless task. That had to stop.

This tutorial is the result: a simple, robust script that automatically pulls notes from a specific Apple Notes folder and saves them as clean, individual text files. It’s a “set it and forget it” solution that bridges the gap between Apple’s convenient ecosystem and the portable, version-controlled world of plain text. Let’s dive in.

Prerequisites

Before we start, make sure you have the following ready. I’m assuming you’re comfortable with the command line.

  • A Mac running a modern version of macOS.
  • Python 3 installed on your system.
  • Familiarity with creating directories and managing files from the terminal.
  • An Apple Notes folder you want to designate for syncing (I use one called “Inbox”).

The Guide: Step-by-Step

Step 1: The Bridge – A Handy CLI Tool

First things first, we need a way for our script to talk to the Notes database. Apple doesn’t provide a native API for this, but the community has built some excellent tools. The one I rely on is a Python package called notes-mac-cli. It’s a command-line interface that can read, create, and export notes.

You’ll need to install this package using pip. A quick search on PyPI for “notes-mac-cli” will get you the exact command. This tool is the backbone of our entire operation.

Step 2: Scaffolding the Python Script

Now, let’s get into the logic. I’ll skip the standard virtualenv setup since you likely have your own workflow for that. Let’s jump straight to the Python logic. Our script will perform four main actions:

  1. Execute the `notes-mac-cli` command to get all notes from a specific folder in JSON format.
  2. Parse that JSON data.
  3. For each note, sanitize its title to create a safe filename.
  4. Clean the note’s content (it’s HTML!) and write it to a new text file.

Here’s the initial setup of our Python file. We need to import the `subprocess` module to run our CLI tool, `os` for path manipulation, `json` to parse the output, and `re` for sanitizing filenames and cleaning HTML.


import subprocess
import json
import os
import re

# --- Configuration ---
# The name of the folder in Apple Notes you want to sync.
NOTES_FOLDER = "Inbox"
# The local directory where you want to save the text files.
OUTPUT_DIR = "synced_notes"
# ---------------------

Pro Tip: In my production setups, I’d pull these configuration variables from a `config.env` file using a library like `python-dotenv` rather than hardcoding them. It makes the script much more portable.

Step 3: The Core Logic – Fetching and Processing

This is where the magic happens. We’ll write a function that calls `notes-mac-cli`, captures its output, and processes the notes. The `–json` flag is critical here; it gives us structured data that’s a dream to work with, rather than trying to parse plain text output.

Once we have the notes, we loop through them. A common gotcha is that note titles can contain characters that are invalid in filenames (like `/` or `:`). We’ll use a regular expression to clean them up. The note body is another trap; it’s HTML. We need to strip out tags like `<div>` and `<br>` to get clean text.


def sanitize_filename(name):
    """Removes characters that are invalid in filenames."""
    # Remove invalid characters
    s = re.sub(r'[\\/*?:"<>|]', "", name)
    # Replace spaces with underscores for cleaner names
    s = s.replace(" ", "_")
    return s

def clean_html(raw_html):
    """A simple function to strip HTML tags."""
    cleanr = re.compile('<.*?>')
    cleantext = re.sub(cleanr, '', raw_html)
    # Apple Notes sometimes uses &nbsp; for spaces
    cleantext = cleantext.replace('&nbsp;', ' ')
    return cleantext.strip()

def sync_notes():
    """Fetches notes and writes them to text files."""
    # Ensure the output directory exists.
    # On your machine, you'd create this directory first.
    if not os.path.isdir(OUTPUT_DIR):
        print(f"Error: Output directory '{OUTPUT_DIR}' not found.")
        print("Please create it before running the script.")
        return # Replaces sys.exit()

    print(f"Syncing notes from folder: '{NOTES_FOLDER}'...")
    
    command = ["notes", "read", NOTES_FOLDER, "--json"]
    
    try:
        result = subprocess.run(command, capture_output=True, text=True, check=True)
        notes = json.loads(result.stdout)
        
        if not notes:
            print("No notes found in that folder.")
            return

        for note in notes:
            title = note.get("name", "Untitled")
            body_html = note.get("body", "")
            
            filename = sanitize_filename(title) + ".txt"
            filepath = os.path.join(OUTPUT_DIR, filename)
            
            content = clean_html(body_html)
            
            with open(filepath, "w", encoding="utf-8") as f:
                f.write(content)
        
        print(f"Successfully synced {len(notes)} notes to '{OUTPUT_DIR}'.")

    except FileNotFoundError:
        print("Error: 'notes-mac-cli' command not found.")
        print("Is the tool installed and in your system's PATH?")
    except subprocess.CalledProcessError as e:
        print(f"Error executing notes command: {e}")
        print(f"Stderr: {e.stderr}")
    except json.JSONDecodeError:
        print("Error: Could not parse JSON output from notes tool.")

if __name__ == "__main__":
    sync_notes()

Step 4: Automating with a Scheduler

A script is only useful if you don’t have to remember to run it. On macOS, the classic tool for this is `cron`. You can set up a `cron` job to run this Python script on a schedule—say, once every hour.

You’ll need to edit your crontab to add a new line. The entry to run the script every hour would look something like this:

0 * * * * python3 your_script_name.py

A word of caution: `cron` jobs run with a very minimal environment. This is a common source of failure. You’ll likely need to provide the full, absolute path to both your `python3` executable (especially if it’s in a virtual environment) and your script for it to work reliably.

Common Pitfalls

Here’s where I usually mess up when setting this up on a new machine:

  • Permissions: The very first time you run this script, macOS will pop up a dialog asking for permission for your terminal or script editor to control the Notes app. You have to grant it. If you miss it, the script will fail silently.
  • Pathing in Cron: This is my number one automation headache. If the script runs manually but fails in `cron`, it’s almost always a path issue. Double-check that you’re using absolute paths in your crontab.
  • Imperfect HTML Cleaning: My `clean_html` function is basic. Apple Notes can have complex formatting, tables, or lists. For more complex notes, you might need a more robust HTML parsing library like BeautifulSoup, but for simple text, regex is perfectly fine.

Conclusion

And that’s it. You now have a fully automated bridge between Apple Notes and a simple folder of text files. This is a powerful starting point. From here, you could have another process pick up these files, commit them to Git, or index them into a search tool. You’ve reclaimed your data from a silo and integrated it into a wider, more flexible workflow. That’s a huge win in my book. Happy scripting.

Darian Vance - Lead Cloud Architect

Darian Vance

Lead Cloud Architect & DevOps Strategist

With over 12 years in system architecture and automation, Darian specializes in simplifying complex cloud infrastructures. An advocate for open-source solutions, he founded TechResolve to provide engineers with actionable, battle-tested troubleshooting guides and robust software alternatives.


🤖 Frequently Asked Questions

âť“ What is the key technical component enabling interaction with Apple Notes in this solution?

The `notes-mac-cli` Python package serves as the crucial command-line interface tool, allowing the script to read and export notes from the Apple Notes database, specifically utilizing its `–json` output for structured data.

âť“ How does this automated syncing method improve upon manual note management?

This scripting method automates the transfer of Apple Notes content into portable, plain text files, eliminating the time-consuming manual copy-pasting process. It integrates Apple’s convenient note-taking into a flexible, version-controlled workflow, unlike siloed manual approaches.

âť“ What are the most frequent challenges when implementing this script on macOS?

Common challenges include granting macOS permissions for the terminal/script to control the Notes app, ensuring correct absolute pathing for the Python executable and script when using `cron`, and potentially needing more robust HTML parsing for highly complex note formatting.

Leave a Reply

Discover more from TechResolve - SaaS Troubleshooting & Software Alternatives

Subscribe now to keep reading and get access to the full archive.

Continue reading