How-to
Getting Started With Python Excel AutomationDeep dive

Automate Excel on macOS with Python

pywin32 and COM do not exist on a Mac, so automating Excel there takes different tools: openpyxl and pandas when you only need the file, xlwings when Excel itself must do the work, AppleScript through osascript for the odd task xlwings does not cover — plus the macOS permission prompts, the Excel sandbox file-access dialog, and scheduling with launchd.

Most Excel automation advice assumes Windows: win32com.client.Dispatch("Excel.Application"), VBA-style COM calls, Task Scheduler. On a Mac none of that exists — there is no COM, and pywin32 will not install. Excel for Mac can still be automated from Python, but the route depends on what the job really needs. Often it needs no Excel at all: reading, writing and formatting .xlsx files works identically on every platform with pandas and openpyxl. When Excel itself must do something — recalculate a model, refresh a query, export with its own renderer — xlwings drives Excel for Mac through Apple's scripting bridge, and plain AppleScript fills the gaps. This page sorts out which to use and covers the macOS-specific hurdles: privacy permissions, the sandbox, and scheduling. It belongs to Automating Excel with COM and pywin32, whose Windows techniques this page translates.

Prerequisites

Bash
pip install xlwings openpyxl pandas

Steps that control Excel need Excel for Mac (Microsoft 365) and are marked as not run, because our build runs on Linux; the file-based step runs anywhere. Examples use a workbook forecast_model.xlsx in your home folder, with an Inputs sheet and formulas on a Model sheet.

Step 1 — Decide whether you need Excel at all

The single biggest simplification on a Mac is noticing that the job is file work:

Python
import pandas as pd
from openpyxl import Workbook

wb = Workbook()
ws = wb.active
ws.title = "Inputs"
ws.append(["Assumption", "Value"])
ws.append(["Growth", 0.04])
ws.append(["Price", 89.0])
wb.save("forecast_model.xlsx")
print(pd.read_excel("forecast_model.xlsx", sheet_name="Inputs"))

pandas and openpyxl read and write .xlsx files without Excel, on macOS exactly as on Windows or a Linux server — formatting, formulas, charts, tables and protection included. If a task is "produce a formatted report" or "read last month's numbers", this is the whole answer, and it is also the only answer that keeps working when the script moves to a server. Excel becomes necessary only when something must be calculated or rendered by Excel: formulas evaluated with Excel's engine, data connections refreshed, Excel's own PDF output. The library choice is covered in Choosing a Python Excel Library.

Which tool for Excel work on a Mac If the job only reads or writes files, use pandas and openpyxl, which need no Excel. If Excel must calculate, refresh or render, use xlwings to drive Excel for Mac. For the occasional command xlwings does not expose, call AppleScript with osascript. What does the job need from Excel? just the file pandas + openpyxl no Excel, any OS Excel's engine xlwings drives Excel for Mac one missing command AppleScript via osascript Excel's scripting dictionary

Step 2 — Drive Excel for Mac with xlwings

xlwings uses the same Python API on macOS and Windows; only its plumbing differs:

Python
import xlwings as xw
from pathlib import Path

path = Path.home() / "forecast_model.xlsx"
app = xw.App(visible=False)
try:
    wb = app.books.open(str(path))
    wb.sheets["Inputs"]["B2"].value = 0.05          # change an input
    app.calculate()                                  # let Excel recalculate
    result = wb.sheets["Model"]["B10"].value
    print("forecast with 5% growth:", result)
    wb.save()
    wb.close()
finally:
    app.quit()

Opening, writing values, recalculating, reading results and saving look exactly like the Windows examples in Read and Write a Live Excel Workbook with xlwings. That portability is the main reason to prefer xlwings over raw AppleScript. The try/finally matters as much on a Mac as on Windows: a script that crashes halfway otherwise leaves an invisible Excel running.

Step 3 — The first-run permission prompts

The first time a Python process controls Excel, macOS asks the user whether Terminal (or your editor, or whichever app launched Python) may control Microsoft Excel. This is macOS's Automation privacy protection:

  • Click OK to allow it; the choice is stored under System Settings › Privacy & Security › Automation, where it can be changed later.
  • If you clicked Don't Allow, scripts fail with an error mentioning that the application is not allowed to send Apple events; enable the checkbox for Excel under the launching app in that settings page.
  • A scheduled job runs under a different parent process than your Terminal, so it may need its own approval — run it once interactively to trigger the prompt.

Excel for Mac is also sandboxed: it can only open files in places it has been granted access to. When a script opens a file in a new folder, Excel may show a Grant File Access dialog and wait. For unattended jobs, keep workbooks in a folder Excel already has access to (opening one file there by hand and granting access usually suffices), so no dialog can block the run.

The hurdles before a Mac script can control Excel The first run triggers the Automation permission prompt for the app that launched Python. Opening a file in a new location can trigger Excel's Grant File Access dialog. Once both are approved, scripts and scheduled jobs run without dialogs. Automation prompt allow Terminal to control Excel Grant File Access Excel sandbox, per folder Unattended runs no dialogs left

Step 4 — The .api escape hatch is different on a Mac

On Windows, .api returns a COM object and VBA-style code works: rng.api.Font.Bold = True. On macOS, .api returns an appscript reference to Excel's AppleScript object model, which uses different names and call style. The xlwings properties hide that difference, so use them wherever they exist:

Python
rng = wb.sheets["Model"]["A1:D1"]
rng.font.bold = True                 # works on both platforms
rng.color = "#E0E7FF"                # works on both platforms
rng.number_format = "#,##0.00"       # works on both platforms

import sys
if sys.platform == "darwin":
    rng.api.font_object.bold.set(True)            # appscript style (macOS)
else:
    rng.api.Font.Bold = True                      # COM style (Windows)

The macOS line is the same call xlwings itself makes internally for font.bold — a property read with .get() and written with .set() on the range's font_object. The rule of thumb: write against xlwings' own API, and keep any .api calls in a small, clearly marked platform branch. Code translated from a recorded VBA macro will almost always be the Windows form. The general formatting properties are listed in Format Cells in a Live Workbook with xlwings.

Excel automation on Windows and macOS Windows offers COM through pywin32 and xlwings, with .api exposing COM objects and Task Scheduler for jobs. macOS has no COM; xlwings works through AppleScript, .api exposes appscript references with different syntax, AppleScript can be called with osascript, permissions and the sandbox must be approved, and launchd schedules jobs. Windows pywin32 / COM available xlwings .api = COM objects VBA-style code translates directly Task Scheduler for jobs macOS no COM, no pywin32 xlwings works the same at its own API level .api = appscript, different syntax privacy prompts, sandbox, launchd

Step 5 — AppleScript from Python for the gaps

For a one-off command that neither xlwings nor openpyxl covers, Excel's AppleScript dictionary is available through the osascript command-line tool:

Python
import subprocess

script = f'''
tell application "Microsoft Excel"
    open POSIX file "{path}"
    calculate full
    save active workbook
    close active workbook saving no
end tell
'''
result = subprocess.run(["osascript", "-e", script], capture_output=True, text=True, timeout=120)
print(result.returncode, result.stdout.strip(), result.stderr.strip())

osascript -e runs the script and returns its output; check returncode and stderr, because AppleScript errors (a missing file, a permission refusal) are reported there rather than as Python exceptions. Excel's dictionary — viewable in Script Editor via File › Open Dictionary — lists every command; build and test scripts there first, then paste them into Python. Keep AppleScript for small gaps: anything substantial is easier to maintain in xlwings, which also runs on Windows.

Step 6 — Schedule it with launchd

The Mac equivalent of Task Scheduler or cron is launchd, configured with a small property-list file in ~/Library/LaunchAgents. Because controlling Excel needs a logged-in user session (Excel is a GUI application), use a LaunchAgent, not a system-wide daemon, and expect the job to run only while that user is logged in. File-only jobs from Step 1 have no such restriction and can run anywhere. The full setup, including the plist and log files, is in Schedule a Python Excel Script on macOS with launchd.

Common pitfalls

  • Installing pywin32 on a Mac. It is Windows-only; use xlwings or file-based libraries.
  • Using Excel when the file is enough. openpyxl and pandas need no permissions, no Excel and no logged-in user.
  • Ignoring the permission prompt. A refused Automation permission makes every script fail until it is re-enabled in System Settings.
  • Files outside Excel's granted folders. The Grant File Access dialog blocks unattended runs; keep files in an approved folder.
  • Windows .api code on a Mac. COM-style calls fail; stick to xlwings' own API or branch by platform.
  • Hard-coded Windows paths. C:\Reports\file.xlsx means nothing on a Mac; build paths with pathlib.Path, as in the examples, so the same script runs on both.

Conclusion

On a Mac, start by asking whether Excel is needed at all — pandas and openpyxl handle file work on every platform. When Excel's engine is required, drive it with xlwings, whose API is the same as on Windows, approve the Automation permission and Excel's file-access prompt once, and keep any .api code in a platform-specific branch. Use AppleScript through osascript for the rare missing command, and schedule GUI-dependent jobs with a launchd LaunchAgent in a logged-in session.

Frequently asked questions

Can I use pywin32 or win32com on a Mac? No. COM and pywin32 exist only on Windows. On macOS, use xlwings to control Excel, or openpyxl and pandas to work with .xlsx files without Excel.

Does xlwings work on macOS? Yes. xlwings drives Excel for Mac through Apple's scripting bridge, and its own API — opening books, reading and writing ranges, formatting, calculating — is the same as on Windows. Only code that uses .api differs.

Why does my Python script say it is not allowed to send Apple events to Excel? macOS blocked the Automation permission for the app that launched Python. Enable Microsoft Excel for that app under System Settings, Privacy & Security, Automation, then run the script again.

How do I schedule an Excel automation script on a Mac? Use launchd with a LaunchAgent plist in ~/Library/LaunchAgents. Jobs that control Excel need the user to be logged in; jobs that only use openpyxl or pandas do not.

More on com & pywin32

More on com & pywin32 →