[{"data":1,"prerenderedAt":1332},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-automate-for-report-workflows":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-automate-for-report-workflows":1298,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-automate-for-report-workflows":1306,"children:\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-automate-for-report-workflows":1311,"related:\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-automate-for-report-workflows":1312},{"id":4,"title":5,"body":6,"dateModified":1264,"datePublished":1264,"description":1265,"extension":1266,"faq":1267,"keyword":1276,"kind":1277,"meta":1278,"navigation":292,"path":1287,"seo":1288,"slug":1291,"stem":1292,"topics":1293,"type":1296,"__hash__":1297},"docs\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-automate-for-report-workflows\u002Findex.md","Python vs Power Automate for Report Workflows",{"type":7,"value":8,"toc":1252},"minimark",[9,19,24,27,204,208,220,231,246,487,490,496,502,780,787,955,959,962,970,974,977,980,984,987,1009,1115,1119,1139,1143,1175,1179,1182,1186,1192,1198,1204,1210,1214,1248],[10,11,12,13,18],"p",{},"In a Microsoft 365 organisation, the question comes up as soon as someone automates a report: build it as a Power Automate flow, which IT already supports and a business user can edit, or as a Python script, which a developer or analyst maintains? Both can watch a folder, read a workbook, build something and email it. They differ in what they are good at, what they cost to run, and who can fix them when they break. This page compares them for recurring report workflows and shows where each fits. It is part of ",[14,15,17],"a",{"href":16},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002F","Choosing a Python Excel Library",".",[20,21,23],"h2",{"id":22},"the-workflow-being-compared","The workflow being compared",[10,25,26],{},"A typical weekly report: every Monday, three regional managers have dropped a sales export into a shared folder. Combine the files, total revenue by region and product, flag products whose units fell by more than 20% on the prior week, write a formatted workbook, and email it to a distribution list.",[28,29,37,38,37,42,37,46,37,54,37,64,37,75,37,82,37,87,37,94,37,99,37,103,37,110,37,115,37,118,37,122,37,125,37,128,37,134,37,139,37,143,37,147,37,151,37,154,37,159,37,166,37,171,37,174,37,177,37,181,37,184,37,189,37,196,37,201],"svg",{"viewBox":30,"role":31,"ariaLabelledBy":32,"xmlns":35,"style":36},"4 0 752 142","img",[33,34],"pa-flow-t","pa-flow-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[39,40,41],"title",{"id":33},"The weekly report workflow",[43,44,45],"desc",{"id":34},"A five-step workflow. A schedule or a new file triggers the run. Regional files are read from a shared folder, combined and summarised, written as a formatted workbook, and emailed to a list.",[47,48],"rect",{"x":49,"y":50,"width":51,"height":52,"fill":53},"4","0","752","142","#ffffff",[47,55],{"x":56,"y":57,"width":58,"height":59,"rx":60,"fill":61,"stroke":62,"style":63},"20.0","16.0","115.2","110.5","12","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[65,66,68],"g",{"transform":67},"translate(65.6,37.8) scale(1.000)",[69,70],"path",{"d":71,"fill":72,"stroke":73,"style":74},"M12 3a9 9 0 1 0 .01 0z M12 7v5l3.5 2","none","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[76,77,81],"text",{"x":78,"y":79,"style":80},"77.6","84.2","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Trigger",[76,83,86],{"x":78,"y":84,"style":85},"103.8","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Monday 07:00",[88,89],"line",{"x1":90,"y1":91,"x2":92,"y2":91,"stroke":93,"style":63},"140.2","71.2","162.2","var(--brand,#5b5cf0)",[95,96],"polygon",{"points":97,"fill":98},"166.2,71.2 157.2,76.2 157.2,66.2","#5b5cf0",[47,100],{"x":101,"y":57,"width":58,"height":59,"rx":60,"fill":102,"stroke":93,"style":63},"171.2","#f0f4ff",[65,104,106],{"transform":105},"translate(216.8,37.8) scale(1.000)",[69,107],{"d":108,"fill":72,"stroke":109,"style":74},"M3 6h6l2 2h10v11H3z","var(--brand-strong,#4338ca)",[76,111,114],{"x":112,"y":79,"style":113},"228.8","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Read files",[76,116,117],{"x":112,"y":84,"style":85},"shared folder",[88,119],{"x1":120,"y1":91,"x2":121,"y2":91,"stroke":93,"style":63},"291.4","313.4",[95,123],{"points":124,"fill":98},"317.4,71.2 308.4,76.2 308.4,66.2",[47,126],{"x":127,"y":57,"width":58,"height":59,"rx":60,"fill":102,"stroke":93,"style":63},"322.4",[65,129,131],{"transform":130},"translate(368.0,29.0) scale(1.000)",[69,132],{"d":133,"fill":72,"stroke":109,"style":74},"M12 3l9 5-9 5-9-5z M3 13l9 5 9-5 M3 17l9 5 9-5",[76,135,138],{"x":136,"y":137,"style":113},"380.0","75.5","Combine and",[76,140,142],{"x":136,"y":141,"style":113},"93.0","summarise",[76,144,146],{"x":136,"y":145,"style":85},"112.5","the actual work",[88,148],{"x1":149,"y1":91,"x2":150,"y2":91,"stroke":93,"style":63},"442.6","464.6",[95,152],{"points":153,"fill":98},"468.6,71.2 459.6,76.2 459.6,66.2",[47,155],{"x":156,"y":57,"width":58,"height":59,"rx":60,"fill":157,"stroke":158,"style":63},"473.6","#d9f4f1","var(--teal,#0f9488)",[65,160,162],{"transform":161},"translate(519.2,29.0) scale(1.000)",[69,163],{"d":164,"fill":72,"stroke":165,"style":74},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6","var(--teal-ink,#0b6157)",[76,167,170],{"x":168,"y":137,"style":169},"531.2","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Write",[76,172,173],{"x":168,"y":141,"style":169},"workbook",[76,175,176],{"x":168,"y":145,"style":85},"formatted",[88,178],{"x1":179,"y1":91,"x2":180,"y2":91,"stroke":93,"style":63},"593.8","615.8",[95,182],{"points":183,"fill":98},"619.8,71.2 610.8,76.2 610.8,66.2",[47,185],{"x":186,"y":57,"width":58,"height":59,"rx":60,"fill":187,"stroke":188,"style":63},"624.8","#fdefd8","var(--gold,#b4740a)",[65,190,192],{"transform":191},"translate(670.4,37.8) scale(1.000)",[69,193],{"d":194,"fill":72,"stroke":195,"style":74},"M3 6h18v12H3z M3 7l9 6 9-6","var(--gold-ink,#7a4e06)",[76,197,200],{"x":198,"y":79,"style":199},"682.4","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Email",[76,202,203],{"x":198,"y":84,"style":85},"distribution list",[20,205,207],{"id":206},"how-each-tool-handles-each-step","How each tool handles each step",[10,209,210,214,215,219],{},[211,212,213],"strong",{},"Triggers."," Power Automate's strength. A cloud flow can start on a schedule, when a file is created in SharePoint or OneDrive, when an email arrives, or from a button, with no server to run. A Python script needs something to start it: cron, Windows Task Scheduler, a cloud scheduler or a job runner, and a machine or container to run on. ",[14,216,218],{"href":217},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Fchoose-a-scheduler-for-python-excel-reports\u002F","Choose a Scheduler for Python Excel Reports"," covers the options.",[10,221,222,225,226,230],{},[211,223,224],{},"Reading Excel data."," Power Automate's Excel Online (Business) connector works with data in ",[227,228,229],"em",{},"Excel tables",", not arbitrary ranges, and \"List rows present in a table\" returns a limited number of rows per call by default; larger tables need its pagination setting, with a threshold that depends on the licence. Each row comes back as JSON, with dates as serial numbers unless you ask for ISO format. Python reads any range, any sheet, thousands of rows per second, with types.",[10,232,233,236,237,241,242,245],{},[211,234,235],{},"Processing."," This is the step that decides most cases. A flow expresses logic as actions: loops, conditions, \"Filter array\", \"Select\", expressions like ",[238,239,240],"code",{},"add()"," and ",[238,243,244],{},"formatDateTime()",". Combining three files and grouping by two columns means nested loops over rows, which is slow and hard to read. The same step in pandas is a few lines:",[247,248,253],"pre",{"className":249,"code":250,"language":251,"meta":252,"style":252},"language-python shiki shiki-themes github-light github-dark-high-contrast","from pathlib import Path\nimport pandas as pd\n\nfiles = sorted(Path(\"inbox\").glob(\"sales_*_2026-09-27.xlsx\"))\nsales = pd.concat([pd.read_excel(f) for f in files], ignore_index=True)\nsummary = sales.pivot_table(index=\"Product\", columns=\"Region\", values=\"Revenue\", aggfunc=\"sum\", margins=True, margins_name=\"Total\")\nprint(f\"{len(files)} files, {len(sales)} rows\")\nprint(summary.round(0).to_string())\n","python","",[238,254,255,273,287,294,323,358,429,474],{"__ignoreMap":252},[256,257,259,263,267,270],"span",{"class":88,"line":258},1,[256,260,262],{"class":261},"s-kum","from",[256,264,266],{"class":265},"skGVy"," pathlib ",[256,268,269],{"class":261},"import",[256,271,272],{"class":265}," Path\n",[256,274,276,278,281,284],{"class":88,"line":275},2,[256,277,269],{"class":261},[256,279,280],{"class":265}," pandas ",[256,282,283],{"class":261},"as",[256,285,286],{"class":265}," pd\n",[256,288,290],{"class":88,"line":289},3,[256,291,293],{"emptyLinePlaceholder":292},true,"\n",[256,295,297,300,303,307,310,314,317,320],{"class":88,"line":296},4,[256,298,299],{"class":265},"files ",[256,301,302],{"class":261},"=",[256,304,306],{"class":305},"sP0c6"," sorted",[256,308,309],{"class":265},"(Path(",[256,311,313],{"class":312},"srMev","\"inbox\"",[256,315,316],{"class":265},").glob(",[256,318,319],{"class":312},"\"sales_*_2026-09-27.xlsx\"",[256,321,322],{"class":265},"))\n",[256,324,326,329,331,334,337,340,343,346,350,352,355],{"class":88,"line":325},5,[256,327,328],{"class":265},"sales ",[256,330,302],{"class":261},[256,332,333],{"class":265}," pd.concat([pd.read_excel(f) ",[256,335,336],{"class":261},"for",[256,338,339],{"class":265}," f ",[256,341,342],{"class":261},"in",[256,344,345],{"class":265}," files], ",[256,347,349],{"class":348},"sa561","ignore_index",[256,351,302],{"class":261},[256,353,354],{"class":305},"True",[256,356,357],{"class":265},")\n",[256,359,361,364,366,369,372,374,377,380,383,385,388,390,393,395,398,400,403,405,408,410,413,415,417,419,422,424,427],{"class":88,"line":360},6,[256,362,363],{"class":265},"summary ",[256,365,302],{"class":261},[256,367,368],{"class":265}," sales.pivot_table(",[256,370,371],{"class":348},"index",[256,373,302],{"class":261},[256,375,376],{"class":312},"\"Product\"",[256,378,379],{"class":265},", ",[256,381,382],{"class":348},"columns",[256,384,302],{"class":261},[256,386,387],{"class":312},"\"Region\"",[256,389,379],{"class":265},[256,391,392],{"class":348},"values",[256,394,302],{"class":261},[256,396,397],{"class":312},"\"Revenue\"",[256,399,379],{"class":265},[256,401,402],{"class":348},"aggfunc",[256,404,302],{"class":261},[256,406,407],{"class":312},"\"sum\"",[256,409,379],{"class":265},[256,411,412],{"class":348},"margins",[256,414,302],{"class":261},[256,416,354],{"class":305},[256,418,379],{"class":265},[256,420,421],{"class":348},"margins_name",[256,423,302],{"class":261},[256,425,426],{"class":312},"\"Total\"",[256,428,357],{"class":265},[256,430,432,435,438,441,444,448,451,454,457,460,462,464,467,469,472],{"class":88,"line":431},7,[256,433,434],{"class":305},"print",[256,436,437],{"class":265},"(",[256,439,440],{"class":261},"f",[256,442,443],{"class":312},"\"",[256,445,447],{"class":446},"sSjpA","{",[256,449,450],{"class":305},"len",[256,452,453],{"class":265},"(files)",[256,455,456],{"class":446},"}",[256,458,459],{"class":312}," files, ",[256,461,447],{"class":446},[256,463,450],{"class":305},[256,465,466],{"class":265},"(sales)",[256,468,456],{"class":446},[256,470,471],{"class":312}," rows\"",[256,473,357],{"class":265},[256,475,477,479,482,484],{"class":88,"line":476},8,[256,478,434],{"class":305},[256,480,481],{"class":265},"(summary.round(",[256,483,50],{"class":305},[256,485,486],{"class":265},").to_string())\n",[10,488,489],{},"The flag for products down more than 20% on the prior week is one more merge against last week's summary, two or three lines of pandas. In a flow it would be another loop over rows with a lookup inside, and another place for an off-by-one in an expression to hide.",[10,491,492,495],{},[211,493,494],{},"Writing the workbook."," A flow can create a file from a template and add rows to a table, one action per row, and run an Office Script for formatting. Python writes the whole formatted workbook in one pass with openpyxl or xlsxwriter, including charts and conditional formats.",[10,497,498,501],{},[211,499,500],{},"Delivery."," Even. The Office 365 Outlook connector sends mail as the flow's owner with a couple of fields; Python needs SMTP or Graph credentials and a few more lines. Building the message itself is simple either way:",[247,503,505],{"className":249,"code":504,"language":251,"meta":252,"style":252},"from email.message import EmailMessage\n\nwith pd.ExcelWriter(\"weekly_sales_2026-09-27.xlsx\", engine=\"xlsxwriter\") as xw:\n    summary.round(2).to_excel(xw, sheet_name=\"Summary\")\n    xw.sheets[\"Summary\"].set_column(1, 4, 12, xw.book.add_format({\"num_format\": \"#,##0\"}))\n\nmsg = EmailMessage()\nmsg[\"Subject\"] = \"Weekly sales, week ending 27 September 2026\"\nmsg[\"From\"] = \"reports@example.com\"\nmsg[\"To\"] = \"sales-leads@example.com\"\nmsg.set_content(f\"Total revenue: {summary.loc['Total', 'Total']:,.0f}. Workbook attached.\")\nmsg.add_attachment(Path(\"weekly_sales_2026-09-27.xlsx\").read_bytes(), maintype=\"application\",\n                   subtype=\"vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n                   filename=\"weekly_sales_2026-09-27.xlsx\")\nprint(msg[\"Subject\"], \"|\", len(msg.as_bytes()), \"bytes\")\n",[238,506,507,519,523,552,573,609,613,623,639,654,669,705,727,740,752],{"__ignoreMap":252},[256,508,509,511,514,516],{"class":88,"line":258},[256,510,262],{"class":261},[256,512,513],{"class":265}," email.message ",[256,515,269],{"class":261},[256,517,518],{"class":265}," EmailMessage\n",[256,520,521],{"class":88,"line":275},[256,522,293],{"emptyLinePlaceholder":292},[256,524,525,528,531,534,536,539,541,544,547,549],{"class":88,"line":289},[256,526,527],{"class":261},"with",[256,529,530],{"class":265}," pd.ExcelWriter(",[256,532,533],{"class":312},"\"weekly_sales_2026-09-27.xlsx\"",[256,535,379],{"class":265},[256,537,538],{"class":348},"engine",[256,540,302],{"class":261},[256,542,543],{"class":312},"\"xlsxwriter\"",[256,545,546],{"class":265},") ",[256,548,283],{"class":261},[256,550,551],{"class":265}," xw:\n",[256,553,554,557,560,563,566,568,571],{"class":88,"line":296},[256,555,556],{"class":265},"    summary.round(",[256,558,559],{"class":305},"2",[256,561,562],{"class":265},").to_excel(xw, ",[256,564,565],{"class":348},"sheet_name",[256,567,302],{"class":261},[256,569,570],{"class":312},"\"Summary\"",[256,572,357],{"class":265},[256,574,575,578,580,583,586,588,590,592,594,597,600,603,606],{"class":88,"line":325},[256,576,577],{"class":265},"    xw.sheets[",[256,579,570],{"class":312},[256,581,582],{"class":265},"].set_column(",[256,584,585],{"class":305},"1",[256,587,379],{"class":265},[256,589,49],{"class":305},[256,591,379],{"class":265},[256,593,60],{"class":305},[256,595,596],{"class":265},", xw.book.add_format({",[256,598,599],{"class":312},"\"num_format\"",[256,601,602],{"class":265},": ",[256,604,605],{"class":312},"\"#,##0\"",[256,607,608],{"class":265},"}))\n",[256,610,611],{"class":88,"line":360},[256,612,293],{"emptyLinePlaceholder":292},[256,614,615,618,620],{"class":88,"line":431},[256,616,617],{"class":265},"msg ",[256,619,302],{"class":261},[256,621,622],{"class":265}," EmailMessage()\n",[256,624,625,628,631,634,636],{"class":88,"line":476},[256,626,627],{"class":265},"msg[",[256,629,630],{"class":312},"\"Subject\"",[256,632,633],{"class":265},"] ",[256,635,302],{"class":261},[256,637,638],{"class":312}," \"Weekly sales, week ending 27 September 2026\"\n",[256,640,642,644,647,649,651],{"class":88,"line":641},9,[256,643,627],{"class":265},[256,645,646],{"class":312},"\"From\"",[256,648,633],{"class":265},[256,650,302],{"class":261},[256,652,653],{"class":312}," \"reports@example.com\"\n",[256,655,657,659,662,664,666],{"class":88,"line":656},10,[256,658,627],{"class":265},[256,660,661],{"class":312},"\"To\"",[256,663,633],{"class":265},[256,665,302],{"class":261},[256,667,668],{"class":312}," \"sales-leads@example.com\"\n",[256,670,672,675,677,680,682,685,688,690,692,695,698,700,703],{"class":88,"line":671},11,[256,673,674],{"class":265},"msg.set_content(",[256,676,440],{"class":261},[256,678,679],{"class":312},"\"Total revenue: ",[256,681,447],{"class":446},[256,683,684],{"class":265},"summary.loc[",[256,686,687],{"class":312},"'Total'",[256,689,379],{"class":265},[256,691,687],{"class":312},[256,693,694],{"class":265},"]",[256,696,697],{"class":261},":,.0f",[256,699,456],{"class":446},[256,701,702],{"class":312},". Workbook attached.\"",[256,704,357],{"class":265},[256,706,708,711,713,716,719,721,724],{"class":88,"line":707},12,[256,709,710],{"class":265},"msg.add_attachment(Path(",[256,712,533],{"class":312},[256,714,715],{"class":265},").read_bytes(), ",[256,717,718],{"class":348},"maintype",[256,720,302],{"class":261},[256,722,723],{"class":312},"\"application\"",[256,725,726],{"class":265},",\n",[256,728,730,733,735,738],{"class":88,"line":729},13,[256,731,732],{"class":348},"                   subtype",[256,734,302],{"class":261},[256,736,737],{"class":312},"\"vnd.openxmlformats-officedocument.spreadsheetml.sheet\"",[256,739,726],{"class":265},[256,741,743,746,748,750],{"class":88,"line":742},14,[256,744,745],{"class":348},"                   filename",[256,747,302],{"class":261},[256,749,533],{"class":312},[256,751,357],{"class":265},[256,753,755,757,760,762,765,768,770,772,775,778],{"class":88,"line":754},15,[256,756,434],{"class":305},[256,758,759],{"class":265},"(msg[",[256,761,630],{"class":312},[256,763,764],{"class":265},"], ",[256,766,767],{"class":312},"\"|\"",[256,769,379],{"class":265},[256,771,450],{"class":305},[256,773,774],{"class":265},"(msg.as_bytes()), ",[256,776,777],{"class":312},"\"bytes\"",[256,779,357],{"class":265},[10,781,782,786],{},[14,783,785],{"href":784},"\u002Fautomating-reporting-workflows\u002Femailing-excel-reports-with-smtplib\u002F","Emailing Excel Reports with smtplib"," covers the sending, including through Microsoft Graph so the mail comes from a Microsoft 365 mailbox like a flow's would.",[28,788,37,794,37,797,37,800,37,805,37,811,37,817,37,821,37,825,37,829,37,833,37,837,37,842,37,844,37,848,37,850,37,853,37,856,37,860,37,862,37,865,37,867,37,870,37,873,37,877,37,879,37,882,37,884,37,887,37,890,37,894,37,896,37,899,37,901,37,904,37,907,37,911,37,913,37,916,37,918,37,921,37,924,37,928,37,930,37,933,37,935,37,938,37,941,37,945,37,947,37,950,37,952],{"viewBox":789,"role":31,"ariaLabelledBy":790,"xmlns":35,"style":793},"156 0 447 256",[791,792],"pa-scores-t","pa-scores-d","width:100%;max-width:447px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[39,795,796],{"id":791},"Where each tool is stronger",[43,798,799],{"id":792},"A comparison table of the workflow steps. Power Automate is stronger at triggers and delivery inside Microsoft 365. Python is stronger at reading arbitrary ranges, processing data and writing formatted workbooks. Error handling and testing favour Python; maintenance by non-developers favours Power Automate.",[47,801],{"x":802,"y":50,"width":803,"height":804,"fill":53},"156","447","256",[47,806],{"x":807,"y":57,"width":808,"height":809,"rx":50,"fill":102,"stroke":93,"style":810},"172.5","167.7","28.0","stroke-width:1.5px",[76,812,816],{"x":813,"y":814,"style":815},"256.4","35.0","font-size:11px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Step",[47,818],{"x":819,"y":57,"width":820,"height":809,"rx":50,"fill":102,"stroke":93,"style":810},"340.3","119.8",[76,822,824],{"x":823,"y":814,"style":815},"400.2","Power Automate",[47,826],{"x":827,"y":57,"width":828,"height":809,"rx":50,"fill":102,"stroke":93,"style":810},"460.1","127.4",[76,830,832],{"x":831,"y":814,"style":815},"523.8","Python",[47,834],{"x":807,"y":835,"width":808,"height":809,"rx":50,"fill":53,"stroke":62,"style":836},"44.0","stroke-width:1px",[76,838,841],{"x":813,"y":839,"style":840},"63.0","font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Triggers and schedules",[47,843],{"x":819,"y":835,"width":820,"height":809,"rx":50,"fill":157,"stroke":158,"style":810},[76,845,847],{"x":823,"y":839,"style":846},"font-size:11px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","built in",[47,849],{"x":827,"y":835,"width":828,"height":809,"rx":50,"fill":53,"stroke":62,"style":836},[76,851,852],{"x":831,"y":839,"style":840},"needs a scheduler",[47,854],{"x":807,"y":855,"width":808,"height":809,"rx":50,"fill":53,"stroke":62,"style":836},"72.0",[76,857,859],{"x":813,"y":858,"style":840},"91.0","Read Excel data",[47,861],{"x":819,"y":855,"width":820,"height":809,"rx":50,"fill":53,"stroke":62,"style":836},[76,863,864],{"x":823,"y":858,"style":840},"tables only",[47,866],{"x":827,"y":855,"width":828,"height":809,"rx":50,"fill":157,"stroke":158,"style":810},[76,868,869],{"x":831,"y":858,"style":846},"any range",[47,871],{"x":807,"y":872,"width":808,"height":809,"rx":50,"fill":53,"stroke":62,"style":836},"100.0",[76,874,876],{"x":813,"y":875,"style":840},"119.0","Combine and summarise",[47,878],{"x":819,"y":872,"width":820,"height":809,"rx":50,"fill":53,"stroke":62,"style":836},[76,880,881],{"x":823,"y":875,"style":840},"loops of actions",[47,883],{"x":827,"y":872,"width":828,"height":809,"rx":50,"fill":157,"stroke":158,"style":810},[76,885,886],{"x":831,"y":875,"style":846},"a few lines",[47,888],{"x":807,"y":889,"width":808,"height":809,"rx":50,"fill":53,"stroke":62,"style":836},"128.0",[76,891,893],{"x":813,"y":892,"style":840},"147.0","Formatted workbook",[47,895],{"x":819,"y":889,"width":820,"height":809,"rx":50,"fill":53,"stroke":62,"style":836},[76,897,898],{"x":823,"y":892,"style":840},"template + script",[47,900],{"x":827,"y":889,"width":828,"height":809,"rx":50,"fill":157,"stroke":158,"style":810},[76,902,903],{"x":831,"y":892,"style":846},"one pass",[47,905],{"x":807,"y":906,"width":808,"height":809,"rx":50,"fill":53,"stroke":62,"style":836},"156.0",[76,908,910],{"x":813,"y":909,"style":840},"175.0","Email in Microsoft 365",[47,912],{"x":819,"y":906,"width":820,"height":809,"rx":50,"fill":157,"stroke":158,"style":810},[76,914,915],{"x":823,"y":909,"style":846},"one action",[47,917],{"x":827,"y":906,"width":828,"height":809,"rx":50,"fill":53,"stroke":62,"style":836},[76,919,920],{"x":831,"y":909,"style":840},"SMTP or Graph",[47,922],{"x":807,"y":923,"width":808,"height":809,"rx":50,"fill":53,"stroke":62,"style":836},"184.0",[76,925,927],{"x":813,"y":926,"style":840},"203.0","Tests and version control",[47,929],{"x":819,"y":923,"width":820,"height":809,"rx":50,"fill":53,"stroke":62,"style":836},[76,931,932],{"x":823,"y":926,"style":840},"limited",[47,934],{"x":827,"y":923,"width":828,"height":809,"rx":50,"fill":157,"stroke":158,"style":810},[76,936,937],{"x":831,"y":926,"style":846},"standard",[47,939],{"x":807,"y":940,"width":808,"height":809,"rx":50,"fill":53,"stroke":62,"style":836},"212.0",[76,942,944],{"x":813,"y":943,"style":840},"231.0","Edited by business users",[47,946],{"x":819,"y":940,"width":820,"height":809,"rx":50,"fill":157,"stroke":158,"style":810},[76,948,949],{"x":823,"y":943,"style":846},"yes",[47,951],{"x":827,"y":940,"width":828,"height":809,"rx":50,"fill":53,"stroke":62,"style":836},[76,953,954],{"x":831,"y":943,"style":840},"rarely",[20,956,958],{"id":957},"reliability-testing-and-change","Reliability, testing and change",[10,960,961],{},"A flow keeps a run history with inputs and outputs for each action, which makes the first diagnosis easy. It has configurable retry policies and \"run after\" settings to handle failures. What it lacks is the engineering around code: unit tests, code review, diffs between versions, and a way to run yesterday's inputs through today's logic on a laptop. Flows can be exported as solution packages and put under source control, but the diff is a large JSON document rather than readable logic.",[10,963,964,965,969],{},"Python has all of that by default, and the burden that comes with it: dependencies to pin, a runtime to patch, logs to keep. ",[14,966,968],{"href":967},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation\u002Fdetect-a-scheduled-report-that-did-not-run\u002F","Detect a Scheduled Report That Did Not Run"," covers the monitoring a script needs and a flow mostly provides.",[20,971,973],{"id":972},"cost-and-ownership","Cost and ownership",[10,975,976],{},"Power Automate is included with many Microsoft 365 plans for standard connectors such as SharePoint, OneDrive, Outlook and Excel Online. Premium connectors, including the generic HTTP action and many databases, need a premium licence per user or per flow. Check your tenant's current plans rather than relying on any list, since Microsoft changes them.",[10,978,979],{},"Python itself costs nothing, but it runs somewhere, and someone has to own that machine or cloud service. The larger cost is usually people: a flow can be adjusted by the analyst who owns the report; a script needs someone who writes Python, and when that person leaves, an undocumented script is harder to hand over than a visible flow.",[20,981,983],{"id":982},"using-both","Using both",[10,985,986],{},"The designs that work best often combine them, each doing what it is good at:",[988,989,990,999,1006],"ul",{},[991,992,993,994,998],"li",{},"A flow triggers on the new file or the schedule, and calls the Python job, for example an Azure Function, as in ",[14,995,997],{"href":996},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Frun-an-excel-report-in-azure-functions\u002F","Run an Excel Report in Azure Functions",". Calling it requires the premium HTTP action.",[991,1000,1001,1002,18],{},"The Python job does the reading, processing and writing, and saves the workbook to SharePoint, as in ",[14,1003,1005],{"href":1004},"\u002Fautomating-reporting-workflows\u002Fpublishing-excel-reports-to-cloud-storage\u002Fupload-an-excel-report-to-sharepoint-with-python\u002F","Upload an Excel Report to SharePoint with Python",[991,1007,1008],{},"A second flow, triggered when the file appears, emails it and posts to Teams, which business users can reconfigure without touching the code.",[28,1010,37,1014,37,1017,37,1020,37,1022,37,1024,37,1028,37,1031,37,1034,37,1036,37,1038,37,1040,37,1046,37,1049,37,1053,37,1056,37,1058,37,1060,37,1062,37,1067,37,1070,37,1072,37,1075,37,1077,37,1079,37,1081,37,1086,37,1088,37,1091,37,1094,37,1096,37,1098,37,1100,37,1106,37,1109,37,1112],{"viewBox":30,"role":31,"ariaLabelledBy":1011,"xmlns":35,"style":36},[1012,1013],"pa-both-t","pa-both-d",[39,1015,1016],{"id":1012},"A combined design",[43,1018,1019],{"id":1013},"A Power Automate flow triggers on a schedule and calls a Python function. The Python job reads the regional files, builds the workbook and saves it to SharePoint. A second flow, triggered by the new file, emails it and posts to Teams.",[47,1021],{"x":49,"y":50,"width":51,"height":52,"fill":53},[47,1023],{"x":56,"y":57,"width":58,"height":59,"rx":60,"fill":157,"stroke":158,"style":63},[65,1025,1026],{"transform":67},[69,1027],{"d":71,"fill":72,"stroke":165,"style":74},[76,1029,1030],{"x":78,"y":79,"style":169},"Flow",[76,1032,1033],{"x":78,"y":84,"style":85},"schedule trigger",[88,1035],{"x1":90,"y1":91,"x2":92,"y2":91,"stroke":93,"style":63},[95,1037],{"points":97,"fill":98},[47,1039],{"x":101,"y":57,"width":58,"height":59,"rx":60,"fill":102,"stroke":93,"style":63},[65,1041,1043],{"transform":1042},"translate(216.8,29.0) scale(1.000)",[69,1044],{"d":1045,"fill":72,"stroke":109,"style":74},"M12 3c-4 0-4 1.6-4 3v2h4.2v1H6c-2 0-3 1.4-3 4s1 4 3 4h2v-2.6c0-1.6 1.3-2.9 2.9-2.9h4.2c1.3 0 2.4-1.1 2.4-2.4V6c0-1.6-1.8-3-5.5-3z M10 5.5h.01 M12 21c4 0 4-1.6 4-3v-2h-4.2v-1H18c2 0 3-1.4 3-4s-1-4-3-4h-2v2.6c0 1.6-1.3 2.9-2.9 2.9H8.9c-1.3 0-2.4 1.1-2.4 2.4V18c0 1.6 1.8 3 5.5 3z M14 18.5h.01",[76,1047,1048],{"x":112,"y":137,"style":113},"Python job",[76,1050,1052],{"x":112,"y":1051,"style":85},"95.0","read and",[76,1054,1055],{"x":112,"y":145,"style":85},"process",[88,1057],{"x1":120,"y1":91,"x2":121,"y2":91,"stroke":93,"style":63},[95,1059],{"points":124,"fill":98},[47,1061],{"x":127,"y":57,"width":58,"height":59,"rx":60,"fill":61,"stroke":62,"style":63},[65,1063,1064],{"transform":130},[69,1065],{"d":1066,"fill":72,"stroke":73,"style":74},"M7 18h10a4 4 0 0 0 .6-8A6 6 0 0 0 6.1 11.4 3.4 3.4 0 0 0 7 18z",[76,1068,1069],{"x":136,"y":137,"style":80},"SharePoint",[76,1071,173],{"x":136,"y":1051,"style":85},[76,1073,1074],{"x":136,"y":145,"style":85},"saved",[88,1076],{"x1":149,"y1":91,"x2":150,"y2":91,"stroke":93,"style":63},[95,1078],{"points":153,"fill":98},[47,1080],{"x":156,"y":57,"width":58,"height":59,"rx":60,"fill":157,"stroke":158,"style":63},[65,1082,1083],{"transform":161},[69,1084],{"d":1085,"fill":72,"stroke":165,"style":74},"M13 2L4 14h7l-1 8 9-12h-7z",[76,1087,1030],{"x":168,"y":137,"style":169},[76,1089,1090],{"x":168,"y":1051,"style":85},"file created",[76,1092,1093],{"x":168,"y":145,"style":85},"trigger",[88,1095],{"x1":179,"y1":91,"x2":180,"y2":91,"stroke":93,"style":63},[95,1097],{"points":183,"fill":98},[47,1099],{"x":186,"y":57,"width":58,"height":59,"rx":60,"fill":187,"stroke":188,"style":63},[65,1101,1103],{"transform":1102},"translate(670.4,29.0) scale(1.000)",[69,1104],{"d":1105,"fill":72,"stroke":195,"style":74},"M3 11l18-8-8 18-2-8z",[76,1107,1108],{"x":198,"y":137,"style":199},"Email +",[76,1110,1111],{"x":198,"y":141,"style":199},"Teams",[76,1113,1114],{"x":198,"y":145,"style":85},"delivery",[20,1116,1118],{"id":1117},"which-to-use","Which to use",[988,1120,1121,1127,1133],{},[991,1122,1123,1126],{},[211,1124,1125],{},"Power Automate alone"," when the data is small and already in Excel tables in Microsoft 365, the logic is simple (copy, filter, notify), and business users need to own and change the workflow.",[991,1128,1129,1132],{},[211,1130,1131],{},"Python alone"," when the processing is substantial, the inputs are files or databases outside Microsoft 365, the output needs rich formatting, or the workflow needs tests and code review.",[991,1134,1135,1138],{},[211,1136,1137],{},"Both"," when the organisation standardises on flows for triggers, approvals and delivery, but the report itself needs real data processing.",[20,1140,1142],{"id":1141},"common-pitfalls","Common pitfalls",[988,1144,1145,1151,1157,1163,1169],{},[991,1146,1147,1150],{},[211,1148,1149],{},"Row loops in a flow."," \"Apply to each\" over thousands of rows is slow and burns through action limits; do bulk work in Python or an Office Script.",[991,1152,1153,1156],{},[211,1154,1155],{},"Data outside tables."," The Excel Online connector reads tables; convert ranges to tables first or read the file with Python.",[991,1158,1159,1162],{},[211,1160,1161],{},"Default row limits."," Enable pagination on \"List rows present in a table\", or large tables are silently truncated.",[991,1164,1165,1168],{},[211,1166,1167],{},"Scripts nobody else can run."," Document how to run the Python job, pin its dependencies and keep it in version control.",[991,1170,1171,1174],{},[211,1172,1173],{},"Flows owned by one person."," A flow tied to a user's account stops when the account is disabled; use a service account or shared ownership.",[20,1176,1178],{"id":1177},"conclusion","Conclusion",[10,1180,1181],{},"Power Automate excels at triggers, approvals and delivery inside Microsoft 365, and lets business users own simple workflows; it struggles with row-level processing, large tables and anything that needs testing. Python excels at reading, transforming and writing Excel data, with the full toolkit of software engineering, but needs a place to run and a developer to maintain it. For report workflows of any size, let a flow trigger and deliver and let Python do the processing in between.",[20,1183,1185],{"id":1184},"frequently-asked-questions","Frequently asked questions",[10,1187,1188,1191],{},[211,1189,1190],{},"Can Power Automate replace a Python Excel script?","\nFor simple workflows on small Excel tables, yes. For combining files, grouping, joining or complex formatting, a flow becomes slow and hard to maintain, and Python is the better tool for that step.",[10,1193,1194,1197],{},[211,1195,1196],{},"Can Power Automate run a Python script?","\nNot directly in a cloud flow. A flow can call a Python job hosted as a web endpoint or function through the premium HTTP action, and a desktop flow can run a script on a Windows machine.",[10,1199,1200,1203],{},[211,1201,1202],{},"Why does my flow only return some rows from an Excel table?","\nThe List rows present in a table action returns a limited number of rows by default. Turn on pagination in the action's settings and set a threshold, within the limit your licence allows.",[10,1205,1206,1209],{},[211,1207,1208],{},"Is Power Automate free?","\nStandard connectors are included with many Microsoft 365 plans; premium connectors such as HTTP require a paid licence. Check your organisation's plan, as the details change.",[20,1211,1213],{"id":1212},"related","Related",[988,1215,1216,1222,1229,1236,1241],{},[991,1217,1218,1219,1221],{},"Up one level: ",[14,1220,17],{"href":16}," — picking the right tool for each job.",[991,1223,1224,1228],{},[14,1225,1227],{"href":1226},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-vba-for-excel-automation\u002F","Python vs VBA for Excel Automation"," — including Office Scripts.",[991,1230,1231,1235],{},[14,1232,1234],{"href":1233},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-query-for-cleaning-excel-data\u002F","Python vs Power Query for Cleaning Excel Data"," — the other Microsoft low-code tool.",[991,1237,1238,1240],{},[14,1239,218],{"href":217}," — where the Python side runs.",[991,1242,1243,1247],{},[14,1244,1246],{"href":1245},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002F","Automating Excel Online with Microsoft Graph"," — the API both approaches use.",[1249,1250,1251],"style",{},"html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":252,"searchDepth":275,"depth":275,"links":1253},[1254,1255,1256,1257,1258,1259,1260,1261,1262,1263],{"id":22,"depth":275,"text":23},{"id":206,"depth":275,"text":207},{"id":957,"depth":275,"text":958},{"id":972,"depth":275,"text":973},{"id":982,"depth":275,"text":983},{"id":1117,"depth":275,"text":1118},{"id":1141,"depth":275,"text":1142},{"id":1177,"depth":275,"text":1178},{"id":1184,"depth":275,"text":1185},{"id":1212,"depth":275,"text":1213},"2026-09-29","Compare a Python script with a Power Automate flow for recurring Excel report workflows: triggers and scheduling, working with Excel data, volume and speed, error handling, testing and version control, licensing and who maintains it — and how the two combine, with Python doing the processing and a flow doing the delivery.","md",[1268,1270,1272,1274],{"q":1190,"a":1269},"For simple workflows on small Excel tables, yes. For combining files, grouping, joining or complex formatting, a flow becomes slow and hard to maintain, and Python is the better tool for that step.",{"q":1196,"a":1271},"Not directly in a cloud flow. A flow can call a Python job hosted as a web endpoint or function through the premium HTTP action, and a desktop flow can run a script on a Windows machine.",{"q":1202,"a":1273},"The List rows present in a table action returns a limited number of rows by default. Turn on pagination in the action's settings and set a threshold, within the limit your licence allows.",{"q":1208,"a":1275},"Standard connectors are included with many Microsoft 365 plans; premium connectors such as HTTP require a paid licence. Check your organisation's plan, as the details change.","python vs power automate for report workflows",null,{"breadcrumb":1279},[1280,1283,1286],{"name":1281,"item":1282},"Home","\u002F",{"name":1284,"item":1285},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":17,"item":16},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-automate-for-report-workflows",{"title":1289,"description":1290},"Python vs Power Automate for Excel Report Automation","Python or Power Automate for Excel report workflows? Triggers, the Excel Online connector and tables, row limits and pagination, data processing, error handling, testing, source control, licensing, and a combined design.","python-vs-power-automate-for-report-workflows","getting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-automate-for-report-workflows\u002Findex",[1294,1295],"choosing-libraries","scheduling","comparison","h012GLaqnAjs0_GTd5tiMiWcW6bperbM6nw7FaWpVDU",[1299,1303],{"title":1300,"path":1301,"stem":1302,"children":-1},"Python vs Office Scripts for Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-office-scripts-for-excel-automation","getting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-office-scripts-for-excel-automation\u002Findex",{"title":1234,"path":1304,"stem":1305,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-query-for-cleaning-excel-data","getting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpython-vs-power-query-for-cleaning-excel-data\u002Findex",[1307,1309],{"path":1308,"title":17},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library",{"path":1310,"title":1284},"\u002Fgetting-started-with-python-excel-automation",[],[1313,1317,1320,1323,1326,1329],{"path":1314,"title":1315,"kind":1316},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fxlwings-vs-pywin32-for-excel-automation","xlwings vs pywin32 for Excel Automation","compare",{"path":1318,"title":1319,"kind":1316},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Fopenpyxl-vs-xlsxwriter-vs-pandas-excelwriter","openpyxl vs xlsxwriter vs pandas.ExcelWriter",{"path":1321,"title":218,"kind":1322},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Fchoose-a-scheduler-for-python-excel-reports","how-to",{"path":1324,"title":1325,"kind":1316},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fgoogle-sheets-vs-excel-files-for-python-automation","Google Sheets vs Excel Files for Python Automation",{"path":1327,"title":1328,"kind":1322},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Flightweight-python-excel-libraries-compared","Lightweight Python Excel Libraries Compared",{"path":1330,"title":1331,"kind":1316},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fopenpyxl-vs-pandas-for-excel-automation","openpyxl vs pandas for Excel Automation",1790972057349]