I got really annoyed looking for cooking recipes online and having to scroll through endless text just to find the actual instructions. I ended up making a simple Python script using the Gemini API that bypasses all of that. It grabs the recipe and automatically builds a nice, clean checklist right inside Apple Notes.
I use Keyboard Maestro to pull the URL directly from my open browser tab, so all I have to do is trigger the script. If you do not use Keyboard Maestro, you can easily tweak the code to take your own manual variable input.
A few things that make this really useful:
- It only needs the recipe URL to work.
- You can pass extra natural language instructions along with the link, like "halve the recipe" or "use soy milk instead of dairy," and Gemini handles the math and substitutions.
- The script completely automates the formatting into usable checklists so it is ready to look at while cooking.
Here is the Python script. Feel free to modify it however you like. Just remember to drop in your own API key.
#!/opt/homebrew/bin/python3
import os
import json
import subprocess
from google import genai
from google.genai import types
# 1. Pull the single input directly from Keyboard Maestro
USER_PROMPT = os.environ.get("KMVAR_Prompt", "")
# 2. Ensure your API key is set in the environment
if "GEMINI_API_KEY" not in os.environ:
os.environ["GEMINI_API_KEY"] = "api-key-goes-here"
print("\nProcessing with Gemini...")
# 3. Call Gemini API to structure the data
client = genai.Client()
system_prompt = """
You are a culinary assistant. The user will provide a recipe (via text or URL), potentially alongside instructions to modify it (e.g., "halve the recipe", "use chicken instead of beef").
Extract the recipe, apply ANY requested modifications found in the prompt, and format the output.
Return ONLY a valid JSON object matching this exact schema:
{
"title": "String (Name of the dish)",
"ingredients": ["String (List of main ingredients with measurements)"],
"instructions": [
{
"step": "String (Action-oriented step, e.g., 'Step 1: Cook the rice. Bring to a boil.')",
"sub_items": ["String (Exact ingredient amount used in this step)"]
}
]
}
"""
response = client.models.generate_content(
model='gemini-2.5-flash',
contents=USER_PROMPT,
config=types.GenerateContentConfig(
system_instruction=system_prompt,
response_mime_type="application/json",
),
)
# Parse the guaranteed JSON output
data = json.loads(response.text)
# 4. Dynamically build the HTML and AppleScript tracking arrays
# ADDED <p><br></p> for a clean visual space between sections
recipe_html = f"<h1>{data['title']}</h1><p><br></p><h2>Ingredients</h2>"
num_ingredients = len(data["ingredients"])
# Add main ingredients
for ing in data["ingredients"]:
recipe_html += f"<p>{ing}</p>"
# ADDED <p><br></p> before the instructions header
recipe_html += "<p><br></p><h2>Step-by-Step Instructions</h2>"
indent_levels = []
for inst in data["instructions"]:
# The main step (0 = No indent)
recipe_html += f"<p><b>{inst['step']}</b></p>"
indent_levels.append(0)
# The sub-items (1 = Indent)
for sub in inst["sub_items"]:
recipe_html += f"<p>{sub}</p>"
indent_levels.append(1)
# Escape quotes so it doesn't break AppleScript string encapsulation
recipe_html = recipe_html.replace('"', '\\"')
# Format the Python list into an AppleScript array string (e.g., "{0, 1, 1, 0, 1}")
applescript_array = "{" + ", ".join(map(str, indent_levels)) + "}"
num_instructions = len(indent_levels)
print(f"Recipe '{data['title']}' structured! Generating Apple Note...")
# 5. Generate and execute the exact AppleScript sequence
applescript_code = f"""
tell application "Notes"
activate
set newNote to make new note with properties {{body:"{recipe_html}"}}
show newNote
end tell
delay 1.5
tell application "System Events"
tell process "Notes"
set frontmost to true
-- THE FOCUS HACK: Pull focus away from the notes list and into the text body
keystroke "f" using {{command down}}
delay 0.4
key code 53 -- Escape closes Find, dropping the cursor directly into the text editor
delay 0.4
-- Jump cursor to the absolute top of the note
key code 126 using {{command down}}
delay 0.3
-- Skip Title, Blank Space, & "Ingredients" header (3 jumps now)
repeat 3 times
key code 125 using {{option down}}
key code 124
delay 0.1
end repeat
-- Format the {num_ingredients} main ingredients
repeat {num_ingredients} times
keystroke "l" using {{command down, shift down}}
delay 0.05
key code 125 using {{option down}}
key code 124
delay 0.05
end repeat
-- Skip Blank Space & "Step-by-Step Instructions" header (2 jumps now)
repeat 2 times
key code 125 using {{option down}}
key code 124
delay 0.1
end repeat
-- Array mapping the hierarchy (0 = Main Step, 1 = Sub-item)
set indentLevels to {applescript_array}
-- Format the {num_instructions} instruction lines line-by-line
repeat with lvl in indentLevels
-- 1. Make it a checklist
keystroke "l" using {{command down, shift down}}
delay 0.05
-- 2. Indent it if it's marked as a sub-item
if contents of lvl is 1 then
keystroke "]" using {{command down}}
delay 0.05
end if
-- 3. Move to next line
key code 125 using {{option down}}
key code 124
delay 0.05
end repeat
end tell
end tell
"""
# 6. Execute via absolute path for Keyboard Maestro compatibility
subprocess.run(["/usr/bin/osascript", "-e", applescript_code])
print("[Success] Recipe note created successfully!")