[{"data":1,"prerenderedAt":2647},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frecalculate-excel-online-workbooks-with-graph-sessions":3,"surround:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frecalculate-excel-online-workbooks-with-graph-sessions":2615,"ancestors:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frecalculate-excel-online-workbooks-with-graph-sessions":2623,"children:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frecalculate-excel-online-workbooks-with-graph-sessions":2629,"related:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frecalculate-excel-online-workbooks-with-graph-sessions":2630},{"id":4,"title":5,"body":6,"dateModified":2581,"datePublished":2581,"description":2582,"extension":2583,"faq":2584,"keyword":2593,"kind":2594,"meta":2595,"navigation":459,"path":2604,"seo":2605,"slug":2608,"stem":2609,"topics":2610,"type":2613,"__hash__":2614},"docs\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frecalculate-excel-online-workbooks-with-graph-sessions\u002Findex.md","Recalculate Excel Online Workbooks with Graph Sessions",{"type":7,"value":8,"toc":2567},"minimark",[9,28,172,177,208,251,255,258,285,383,387,402,961,968,972,979,1269,1283,1376,1380,1383,1685,1693,1697,1716,2045,2067,2071,2078,2340,2350,2354,2457,2461,2475,2479,2497,2501,2507,2513,2519,2525,2529,2563],[10,11,12,13,17,18,21,22,27],"p",{},"Plenty of the logic behind a report lives in formulas somebody built years ago: a pricing model, a\ncommission calculator, a forecast with a dozen assumptions on an Inputs sheet. Rewriting that in pandas is\nrisky and slow to sign off. The Graph workbook API lets a script use the model as it is — write the inputs,\nlet Excel's online calculation engine do the work, and read the outputs — with no copy of Excel installed\nanywhere. Doing that reliably depends on two features: ",[14,15,16],"strong",{},"workbook sessions",", which keep the workbook open\nbetween calls and decide whether changes are saved, and the ",[14,19,20],{},"calculate"," action. This guide from\n",[23,24,26],"a",{"href":25},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002F","Automating Excel Online with Microsoft Graph","\ncovers both.",[29,30,38,39,38,43,38,47,38,54,38,63,38,70,38,75,38,80,38,87,38,89,38,95,38,99,38,104,38,108,38,113,38,117,38,121,38,124,38,127,38,131,38,134,38,137,38,141,38,144,38,147,38,151,38,154,38,157,38,161,38,164,38,167],"svg",{"viewBox":31,"role":32,"ariaLabelledBy":33,"xmlns":36,"style":37},"0 0 640 454","img",[34,35],"ses-seq-t","ses-seq-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:640px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[40,41,42],"title",{"id":34},"A what-if calculation inside a workbook session",[44,45,46],"desc",{"id":35},"The script creates a session, writes input values, asks the service to recalculate, reads the output range, and closes the session. With persistChanges false, nothing is saved to the file.",[48,49],"rect",{"x":50,"y":50,"width":51,"height":52,"fill":53},"0","640","454","#ffffff",[48,55],{"x":56,"y":56,"width":57,"height":58,"rx":59,"fill":60,"stroke":61,"style":62},"20","180","40","10","#d9f4f1","var(--teal,#0f9488)","stroke-width:2px",[64,65,69],"text",{"x":66,"y":67,"style":68},"110","45","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Python script",[48,71],{"x":72,"y":56,"width":57,"height":58,"rx":59,"fill":73,"stroke":74,"style":62},"440","#f0f4ff","var(--brand,#5b5cf0)",[64,76,79],{"x":77,"y":67,"style":78},"530","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Graph workbook API",[81,82],"line",{"x1":66,"y1":83,"x2":66,"y2":84,"stroke":85,"style":86},"60","408","var(--line,#cdd5e6)","stroke-width:2px;stroke-dasharray:5 5",[81,88],{"x1":77,"y1":83,"x2":77,"y2":84,"stroke":85,"style":86},[64,90,94],{"x":91,"y":92,"style":93},"320","101","font-size:11.5px;font-weight:600;fill:var(--text,#172033);text-anchor:middle","POST createSession",[81,96],{"x1":97,"y1":66,"x2":98,"y2":66,"stroke":74,"style":62},"114","522",[100,101],"polygon",{"points":102,"fill":103},"527,110 518,105 518,115","#5b5cf0",[64,105,107],{"x":91,"y":106,"style":93},"147","session id",[81,109],{"x1":110,"y1":111,"x2":112,"y2":111,"stroke":61,"style":62},"526","156","118",[100,114],{"points":115,"fill":116},"113,156 122,151 122,161","#0f9488",[64,118,120],{"x":91,"y":119,"style":93},"193","PATCH inputs range",[81,122],{"x1":97,"y1":123,"x2":98,"y2":123,"stroke":74,"style":62},"202",[100,125],{"points":126,"fill":103},"527,202 518,197 518,207",[64,128,130],{"x":91,"y":129,"style":93},"239","POST application\u002Fcalculate",[81,132],{"x1":97,"y1":133,"x2":98,"y2":133,"stroke":74,"style":62},"248",[100,135],{"points":136,"fill":103},"527,248 518,243 518,253",[64,138,140],{"x":91,"y":139,"style":93},"285","GET outputs range",[81,142],{"x1":97,"y1":143,"x2":98,"y2":143,"stroke":74,"style":62},"294",[100,145],{"points":146,"fill":103},"527,294 518,289 518,299",[64,148,150],{"x":91,"y":149,"style":93},"331","calculated values",[81,152],{"x1":110,"y1":153,"x2":112,"y2":153,"stroke":61,"style":62},"340",[100,155],{"points":156,"fill":116},"113,340 122,335 122,345",[64,158,160],{"x":91,"y":159,"style":93},"377","POST closeSession",[81,162],{"x1":97,"y1":163,"x2":98,"y2":163,"stroke":74,"style":62},"386",[100,165],{"points":166,"fill":103},"527,386 518,381 518,391",[64,168,171],{"x":91,"y":169,"style":170},"434","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","every call in between carries the workbook-session-id header",[173,174,176],"h2",{"id":175},"prerequisites","Prerequisites",[178,179,184],"pre",{"className":180,"code":181,"language":182,"meta":183,"style":183},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install msal requests pandas\n","bash","",[185,186,187],"code",{"__ignoreMap":183},[188,189,191,195,199,202,205],"span",{"class":81,"line":190},1,[188,192,194],{"class":193},"sMTad","pip",[188,196,198],{"class":197},"srMev"," install",[188,200,201],{"class":197}," msal",[188,203,204],{"class":197}," requests",[188,206,207],{"class":197}," pandas\n",[209,210,211,228],"ul",{},[212,213,214,215,218,219,222,223,227],"li",{},"A ",[14,216,217],{},"delegated"," token for a work or school account with ",[185,220,221],{},"Files.ReadWrite",". The calculate action is listed as not supported for\npersonal Microsoft accounts and for application permissions, so neither a consumer OneDrive nor an app-only token will work here.\n",[23,224,226],{"href":225},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fauthenticate-to-microsoft-graph-with-msal-for-excel-automation\u002F","Authenticate to Microsoft Graph with MSAL for Excel Automation","\nsets one up.",[212,229,230,231,234,235,238,239,242,243,246,247,250],{},"An ",[185,232,233],{},".xlsx"," workbook in OneDrive for Business or SharePoint with an input range and formula-driven outputs. The examples use an\n",[185,236,237],{},"Inputs"," sheet with assumptions in ",[185,240,241],{},"B2:B4"," and a ",[185,244,245],{},"Model"," sheet with results in ",[185,248,249],{},"A1:B3",".",[173,252,254],{"id":253},"step-1-understand-the-three-ways-to-call-the-workbook-api","Step 1 — Understand the three ways to call the workbook API",[10,256,257],{},"Every workbook request runs in one of three modes, chosen by whether a session exists and how it was created.",[209,259,260,270,279],{},[212,261,262,265,266,269],{},[14,263,264],{},"Persistent session"," (",[185,267,268],{},"persistChanges: true",") — changes are saved to the file, and the workbook stays open in the service between\ncalls, which is much faster for a sequence of requests.",[212,271,272,265,275,278],{},[14,273,274],{},"Non-persistent session",[185,276,277],{},"persistChanges: false",") — changes are visible to later calls in the same session but are discarded when\nthe session ends. Nothing reaches the file.",[212,280,281,284],{},[14,282,283],{},"Sessionless"," — no header at all. Each request opens the workbook, performs the operation, saves and closes it. Fine for a single\ncall; slow and less predictable for a sequence.",[29,286,38,292,38,295,38,298,38,302,38,307,38,312,38,318,38,322,38,325,38,329,38,332,38,335,38,339,38,343,38,345,38,348,38,351,38,354,38,358,38,363,38,367,38,370,38,373,38,376,38,379],{"viewBox":287,"role":32,"ariaLabelledBy":288,"xmlns":36,"style":291},"0 0 760 214",[289,290],"ses-modes-t","ses-modes-d","width:100%;max-width:760px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[40,293,294],{"id":289},"Workbook session modes compared",[44,296,297],{"id":290},"A persistent session saves changes and keeps the workbook open between calls. A non-persistent session keeps changes only for its own lifetime and saves nothing. Sessionless calls open and save the workbook on every request.",[48,299],{"x":50,"y":50,"width":300,"height":301,"fill":53},"760","214",[48,303],{"x":56,"y":56,"width":304,"height":305,"rx":306,"fill":60,"stroke":61,"style":62},"226.7","150","14",[64,308,311],{"x":309,"y":310,"style":68},"133.3","46","persistent",[81,313],{"x1":314,"y1":315,"x2":316,"y2":315,"stroke":61,"style":317},"36","56","230.7","stroke-width:1px",[64,319,268],{"x":309,"y":320,"style":321},"78","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[64,323,324],{"x":309,"y":92,"style":321},"changes saved to file",[64,326,328],{"x":309,"y":327,"style":321},"124","fast multi-call jobs",[64,330,331],{"x":309,"y":106,"style":321},"feeds and updates",[48,333],{"x":334,"y":56,"width":304,"height":305,"rx":306,"fill":73,"stroke":74,"style":62},"266.7",[64,336,338],{"x":337,"y":310,"style":78},"380","non-persistent",[81,340],{"x1":341,"y1":315,"x2":342,"y2":315,"stroke":74,"style":317},"282.7","477.3",[64,344,277],{"x":337,"y":320,"style":321},[64,346,347],{"x":337,"y":92,"style":321},"changes discarded at end",[64,349,350],{"x":337,"y":327,"style":321},"what-if calculations",[64,352,353],{"x":337,"y":106,"style":321},"shared models, no edits",[48,355],{"x":356,"y":56,"width":304,"height":305,"rx":306,"fill":357,"stroke":85,"style":62},"513.3","#e7ebef",[64,359,362],{"x":360,"y":310,"style":361},"626.7","font-size:13px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","sessionless",[81,364],{"x1":365,"y1":315,"x2":366,"y2":315,"stroke":85,"style":317},"529.3","724",[64,368,369],{"x":360,"y":320,"style":321},"no session header",[64,371,372],{"x":360,"y":92,"style":321},"open, save, close per call",[64,374,375],{"x":360,"y":327,"style":321},"one-off single calls",[64,377,378],{"x":360,"y":106,"style":321},"slowest for sequences",[64,380,382],{"x":337,"y":381,"style":170},"200","more than a couple of calls? create a session",[173,384,386],{"id":385},"step-2-wrap-the-session-in-a-context-manager","Step 2 — Wrap the session in a context manager",[10,388,389,390,393,394,397,398,401],{},"A session must be closed, including when something in the middle fails. A context manager guarantees the ",[185,391,392],{},"closeSession"," call and sets the\n",[185,395,396],{},"workbook-session-id"," header on the shared ",[185,399,400],{},"requests.Session"," for everything inside the block.",[178,403,407],{"className":404,"code":405,"language":406,"meta":183,"style":183},"language-python shiki shiki-themes github-light github-dark-high-contrast","import os\nfrom contextlib import contextmanager\nfrom urllib.parse import quote\nimport requests\n\nGRAPH = \"https:\u002F\u002Fgraph.microsoft.com\u002Fv1.0\"\nBOOK = f\"{GRAPH}\u002Fdrives\u002F{os.environ['GRAPH_DRIVE_ID']}\u002Fitems\u002F{os.environ['GRAPH_ITEM_ID']}\u002Fworkbook\"\n\nhttp = requests.Session()\nhttp.headers[\"Authorization\"] = \"Bearer \" + os.environ.get(\"GRAPH_DELEGATED_TOKEN\", \"token\")\n\ndef call(method: str, url: str, **kwargs):\n    response = http.request(method, url, timeout=120, **kwargs)\n    if response.status_code >= 400:\n        raise RuntimeError(f\"{method} {url} -> {response.status_code}: {response.text[:400]}\")\n    return response\n\n@contextmanager\ndef workbook_session(persist: bool):\n    created = call(\"POST\", f\"{BOOK}\u002FcreateSession\", json={\"persistChanges\": persist}).json()\n    http.headers[\"workbook-session-id\"] = created[\"id\"]\n    try:\n        yield created[\"id\"]\n    finally:\n        try:\n            call(\"POST\", f\"{BOOK}\u002FcloseSession\", json={})\n        finally:\n            http.headers.pop(\"workbook-session-id\", None)\n\nwith workbook_session(persist=False) as session_id:\n    print(\"session\", session_id)\n","python",[185,408,409,419,433,446,454,461,474,530,535,547,582,587,616,643,661,722,731,736,742,759,803,825,833,845,853,861,893,901,916,921,947],{"__ignoreMap":183},[188,410,411,415],{"class":81,"line":190},[188,412,414],{"class":413},"s-kum","import",[188,416,418],{"class":417},"skGVy"," os\n",[188,420,422,425,428,430],{"class":81,"line":421},2,[188,423,424],{"class":413},"from",[188,426,427],{"class":417}," contextlib ",[188,429,414],{"class":413},[188,431,432],{"class":417}," contextmanager\n",[188,434,436,438,441,443],{"class":81,"line":435},3,[188,437,424],{"class":413},[188,439,440],{"class":417}," urllib.parse ",[188,442,414],{"class":413},[188,444,445],{"class":417}," quote\n",[188,447,449,451],{"class":81,"line":448},4,[188,450,414],{"class":413},[188,452,453],{"class":417}," requests\n",[188,455,457],{"class":81,"line":456},5,[188,458,460],{"emptyLinePlaceholder":459},true,"\n",[188,462,464,468,471],{"class":81,"line":463},6,[188,465,467],{"class":466},"sP0c6","GRAPH",[188,469,470],{"class":413}," =",[188,472,473],{"class":197}," \"https:\u002F\u002Fgraph.microsoft.com\u002Fv1.0\"\n",[188,475,477,480,482,485,488,492,494,497,500,502,505,508,511,513,516,518,520,523,525,527],{"class":81,"line":476},7,[188,478,479],{"class":466},"BOOK",[188,481,470],{"class":413},[188,483,484],{"class":413}," f",[188,486,487],{"class":197},"\"",[188,489,491],{"class":490},"sSjpA","{",[188,493,467],{"class":466},[188,495,496],{"class":490},"}",[188,498,499],{"class":197},"\u002Fdrives\u002F",[188,501,491],{"class":490},[188,503,504],{"class":417},"os.environ[",[188,506,507],{"class":197},"'GRAPH_DRIVE_ID'",[188,509,510],{"class":417},"]",[188,512,496],{"class":490},[188,514,515],{"class":197},"\u002Fitems\u002F",[188,517,491],{"class":490},[188,519,504],{"class":417},[188,521,522],{"class":197},"'GRAPH_ITEM_ID'",[188,524,510],{"class":417},[188,526,496],{"class":490},[188,528,529],{"class":197},"\u002Fworkbook\"\n",[188,531,533],{"class":81,"line":532},8,[188,534,460],{"emptyLinePlaceholder":459},[188,536,538,541,544],{"class":81,"line":537},9,[188,539,540],{"class":417},"http ",[188,542,543],{"class":413},"=",[188,545,546],{"class":417}," requests.Session()\n",[188,548,550,553,556,559,561,564,567,570,573,576,579],{"class":81,"line":549},10,[188,551,552],{"class":417},"http.headers[",[188,554,555],{"class":197},"\"Authorization\"",[188,557,558],{"class":417},"] ",[188,560,543],{"class":413},[188,562,563],{"class":197}," \"Bearer \"",[188,565,566],{"class":413}," +",[188,568,569],{"class":417}," os.environ.get(",[188,571,572],{"class":197},"\"GRAPH_DELEGATED_TOKEN\"",[188,574,575],{"class":417},", ",[188,577,578],{"class":197},"\"token\"",[188,580,581],{"class":417},")\n",[188,583,585],{"class":81,"line":584},11,[188,586,460],{"emptyLinePlaceholder":459},[188,588,590,593,597,600,603,606,608,610,613],{"class":81,"line":589},12,[188,591,592],{"class":413},"def",[188,594,596],{"class":595},"s_Opv"," call",[188,598,599],{"class":417},"(method: ",[188,601,602],{"class":466},"str",[188,604,605],{"class":417},", url: ",[188,607,602],{"class":466},[188,609,575],{"class":417},[188,611,612],{"class":413},"**",[188,614,615],{"class":417},"kwargs):\n",[188,617,619,622,624,627,631,633,636,638,640],{"class":81,"line":618},13,[188,620,621],{"class":417},"    response ",[188,623,543],{"class":413},[188,625,626],{"class":417}," http.request(method, url, ",[188,628,630],{"class":629},"sa561","timeout",[188,632,543],{"class":413},[188,634,635],{"class":466},"120",[188,637,575],{"class":417},[188,639,612],{"class":413},[188,641,642],{"class":417},"kwargs)\n",[188,644,646,649,652,655,658],{"class":81,"line":645},14,[188,647,648],{"class":413},"    if",[188,650,651],{"class":417}," response.status_code ",[188,653,654],{"class":413},">=",[188,656,657],{"class":466}," 400",[188,659,660],{"class":417},":\n",[188,662,664,667,670,673,676,678,680,683,685,688,691,693,696,698,701,703,706,708,711,714,716,718,720],{"class":81,"line":663},15,[188,665,666],{"class":413},"        raise",[188,668,669],{"class":466}," RuntimeError",[188,671,672],{"class":417},"(",[188,674,675],{"class":413},"f",[188,677,487],{"class":197},[188,679,491],{"class":490},[188,681,682],{"class":417},"method",[188,684,496],{"class":490},[188,686,687],{"class":490}," {",[188,689,690],{"class":417},"url",[188,692,496],{"class":490},[188,694,695],{"class":197}," -> ",[188,697,491],{"class":490},[188,699,700],{"class":417},"response.status_code",[188,702,496],{"class":490},[188,704,705],{"class":197},": ",[188,707,491],{"class":490},[188,709,710],{"class":417},"response.text[:",[188,712,713],{"class":466},"400",[188,715,510],{"class":417},[188,717,496],{"class":490},[188,719,487],{"class":197},[188,721,581],{"class":417},[188,723,725,728],{"class":81,"line":724},16,[188,726,727],{"class":413},"    return",[188,729,730],{"class":417}," response\n",[188,732,734],{"class":81,"line":733},17,[188,735,460],{"emptyLinePlaceholder":459},[188,737,739],{"class":81,"line":738},18,[188,740,741],{"class":595},"@contextmanager\n",[188,743,745,747,750,753,756],{"class":81,"line":744},19,[188,746,592],{"class":413},[188,748,749],{"class":595}," workbook_session",[188,751,752],{"class":417},"(persist: ",[188,754,755],{"class":466},"bool",[188,757,758],{"class":417},"):\n",[188,760,762,765,767,770,773,775,777,779,781,783,785,788,790,793,795,797,800],{"class":81,"line":761},20,[188,763,764],{"class":417},"    created ",[188,766,543],{"class":413},[188,768,769],{"class":417}," call(",[188,771,772],{"class":197},"\"POST\"",[188,774,575],{"class":417},[188,776,675],{"class":413},[188,778,487],{"class":197},[188,780,491],{"class":490},[188,782,479],{"class":466},[188,784,496],{"class":490},[188,786,787],{"class":197},"\u002FcreateSession\"",[188,789,575],{"class":417},[188,791,792],{"class":629},"json",[188,794,543],{"class":413},[188,796,491],{"class":417},[188,798,799],{"class":197},"\"persistChanges\"",[188,801,802],{"class":417},": persist}).json()\n",[188,804,806,809,812,814,816,819,822],{"class":81,"line":805},21,[188,807,808],{"class":417},"    http.headers[",[188,810,811],{"class":197},"\"workbook-session-id\"",[188,813,558],{"class":417},[188,815,543],{"class":413},[188,817,818],{"class":417}," created[",[188,820,821],{"class":197},"\"id\"",[188,823,824],{"class":417},"]\n",[188,826,828,831],{"class":81,"line":827},22,[188,829,830],{"class":413},"    try",[188,832,660],{"class":417},[188,834,836,839,841,843],{"class":81,"line":835},23,[188,837,838],{"class":413},"        yield",[188,840,818],{"class":417},[188,842,821],{"class":197},[188,844,824],{"class":417},[188,846,848,851],{"class":81,"line":847},24,[188,849,850],{"class":413},"    finally",[188,852,660],{"class":417},[188,854,856,859],{"class":81,"line":855},25,[188,857,858],{"class":413},"        try",[188,860,660],{"class":417},[188,862,864,867,869,871,873,875,877,879,881,884,886,888,890],{"class":81,"line":863},26,[188,865,866],{"class":417},"            call(",[188,868,772],{"class":197},[188,870,575],{"class":417},[188,872,675],{"class":413},[188,874,487],{"class":197},[188,876,491],{"class":490},[188,878,479],{"class":466},[188,880,496],{"class":490},[188,882,883],{"class":197},"\u002FcloseSession\"",[188,885,575],{"class":417},[188,887,792],{"class":629},[188,889,543],{"class":413},[188,891,892],{"class":417},"{})\n",[188,894,896,899],{"class":81,"line":895},27,[188,897,898],{"class":413},"        finally",[188,900,660],{"class":417},[188,902,904,907,909,911,914],{"class":81,"line":903},28,[188,905,906],{"class":417},"            http.headers.pop(",[188,908,811],{"class":197},[188,910,575],{"class":417},[188,912,913],{"class":466},"None",[188,915,581],{"class":417},[188,917,919],{"class":81,"line":918},29,[188,920,460],{"emptyLinePlaceholder":459},[188,922,924,927,930,933,935,938,941,944],{"class":81,"line":923},30,[188,925,926],{"class":413},"with",[188,928,929],{"class":417}," workbook_session(",[188,931,932],{"class":629},"persist",[188,934,543],{"class":413},[188,936,937],{"class":466},"False",[188,939,940],{"class":417},") ",[188,942,943],{"class":413},"as",[188,945,946],{"class":417}," session_id:\n",[188,948,950,953,955,958],{"class":81,"line":949},31,[188,951,952],{"class":466},"    print",[188,954,672],{"class":417},[188,956,957],{"class":197},"\"session\"",[188,959,960],{"class":417},", session_id)\n",[10,962,963,964,967],{},"Closing releases the workbook in the service promptly. Sessions also expire on their own after a few minutes without requests, so a job that\npauses between calls — waiting on a database query, say — should call ",[185,965,966],{},"POST \u002Fworkbook\u002FrefreshSession"," periodically, or simply create a new\nsession after the pause.",[173,969,971],{"id":970},"step-3-force-a-recalculation-before-reading-results","Step 3 — Force a recalculation before reading results",[10,973,974,975,978],{},"If the workbook is in automatic calculation mode, dependent formulas update when you write inputs. Many financial models are set to ",[14,976,977],{},"manual","\ncalculation to keep them responsive, and then written inputs do not change any outputs until something recalculates. The calculate action does\nthat explicitly, with three levels.",[178,980,982],{"className":404,"code":981,"language":406,"meta":183,"style":183},"def recalculate(kind: str = \"Recalculate\") -> None:\n    if kind not in {\"Recalculate\", \"Full\", \"FullRebuild\"}:\n        raise ValueError(kind)\n    call(\"POST\", f\"{BOOK}\u002Fapplication\u002Fcalculate\", json={\"calculationType\": kind})   # 204 No Content\n\ndef sheet(name: str) -> str:\n    return f\"{BOOK}\u002Fworksheets\u002F{quote(name)}\"\n\nwith workbook_session(persist=True):\n    call(\"PATCH\", f\"{sheet('Inputs')}\u002Frange(address='B2:B4')\", json={\"values\": [[0.05], [1200], [0.18]]})\n    recalculate()\n    outputs = call(\"GET\", f\"{sheet('Model')}\u002Frange(address='A1:B3')\").json()[\"values\"]\nprint(outputs)\n",[185,983,984,1008,1039,1049,1089,1093,1111,1138,1142,1157,1217,1222,1261],{"__ignoreMap":183},[188,985,986,988,991,994,996,998,1001,1004,1006],{"class":81,"line":190},[188,987,592],{"class":413},[188,989,990],{"class":595}," recalculate",[188,992,993],{"class":417},"(kind: ",[188,995,602],{"class":466},[188,997,470],{"class":413},[188,999,1000],{"class":197}," \"Recalculate\"",[188,1002,1003],{"class":417},") -> ",[188,1005,913],{"class":466},[188,1007,660],{"class":417},[188,1009,1010,1012,1015,1018,1021,1023,1026,1028,1031,1033,1036],{"class":81,"line":421},[188,1011,648],{"class":413},[188,1013,1014],{"class":417}," kind ",[188,1016,1017],{"class":413},"not",[188,1019,1020],{"class":413}," in",[188,1022,687],{"class":417},[188,1024,1025],{"class":197},"\"Recalculate\"",[188,1027,575],{"class":417},[188,1029,1030],{"class":197},"\"Full\"",[188,1032,575],{"class":417},[188,1034,1035],{"class":197},"\"FullRebuild\"",[188,1037,1038],{"class":417},"}:\n",[188,1040,1041,1043,1046],{"class":81,"line":435},[188,1042,666],{"class":413},[188,1044,1045],{"class":466}," ValueError",[188,1047,1048],{"class":417},"(kind)\n",[188,1050,1051,1054,1056,1058,1060,1062,1064,1066,1068,1071,1073,1075,1077,1079,1082,1085],{"class":81,"line":448},[188,1052,1053],{"class":417},"    call(",[188,1055,772],{"class":197},[188,1057,575],{"class":417},[188,1059,675],{"class":413},[188,1061,487],{"class":197},[188,1063,491],{"class":490},[188,1065,479],{"class":466},[188,1067,496],{"class":490},[188,1069,1070],{"class":197},"\u002Fapplication\u002Fcalculate\"",[188,1072,575],{"class":417},[188,1074,792],{"class":629},[188,1076,543],{"class":413},[188,1078,491],{"class":417},[188,1080,1081],{"class":197},"\"calculationType\"",[188,1083,1084],{"class":417},": kind})   ",[188,1086,1088],{"class":1087},"s-wDw","# 204 No Content\n",[188,1090,1091],{"class":81,"line":456},[188,1092,460],{"emptyLinePlaceholder":459},[188,1094,1095,1097,1100,1103,1105,1107,1109],{"class":81,"line":463},[188,1096,592],{"class":413},[188,1098,1099],{"class":595}," sheet",[188,1101,1102],{"class":417},"(name: ",[188,1104,602],{"class":466},[188,1106,1003],{"class":417},[188,1108,602],{"class":466},[188,1110,660],{"class":417},[188,1112,1113,1115,1117,1119,1121,1123,1125,1128,1130,1133,1135],{"class":81,"line":476},[188,1114,727],{"class":413},[188,1116,484],{"class":413},[188,1118,487],{"class":197},[188,1120,491],{"class":490},[188,1122,479],{"class":466},[188,1124,496],{"class":490},[188,1126,1127],{"class":197},"\u002Fworksheets\u002F",[188,1129,491],{"class":490},[188,1131,1132],{"class":417},"quote(name)",[188,1134,496],{"class":490},[188,1136,1137],{"class":197},"\"\n",[188,1139,1140],{"class":81,"line":532},[188,1141,460],{"emptyLinePlaceholder":459},[188,1143,1144,1146,1148,1150,1152,1155],{"class":81,"line":537},[188,1145,926],{"class":413},[188,1147,929],{"class":417},[188,1149,932],{"class":629},[188,1151,543],{"class":413},[188,1153,1154],{"class":466},"True",[188,1156,758],{"class":417},[188,1158,1159,1161,1164,1166,1168,1170,1172,1175,1178,1181,1183,1186,1188,1190,1192,1194,1197,1200,1203,1206,1209,1211,1214],{"class":81,"line":549},[188,1160,1053],{"class":417},[188,1162,1163],{"class":197},"\"PATCH\"",[188,1165,575],{"class":417},[188,1167,675],{"class":413},[188,1169,487],{"class":197},[188,1171,491],{"class":490},[188,1173,1174],{"class":417},"sheet(",[188,1176,1177],{"class":197},"'Inputs'",[188,1179,1180],{"class":417},")",[188,1182,496],{"class":490},[188,1184,1185],{"class":197},"\u002Frange(address='B2:B4')\"",[188,1187,575],{"class":417},[188,1189,792],{"class":629},[188,1191,543],{"class":413},[188,1193,491],{"class":417},[188,1195,1196],{"class":197},"\"values\"",[188,1198,1199],{"class":417},": [[",[188,1201,1202],{"class":466},"0.05",[188,1204,1205],{"class":417},"], [",[188,1207,1208],{"class":466},"1200",[188,1210,1205],{"class":417},[188,1212,1213],{"class":466},"0.18",[188,1215,1216],{"class":417},"]]})\n",[188,1218,1219],{"class":81,"line":584},[188,1220,1221],{"class":417},"    recalculate()\n",[188,1223,1224,1227,1229,1231,1234,1236,1238,1240,1242,1244,1247,1249,1251,1254,1257,1259],{"class":81,"line":589},[188,1225,1226],{"class":417},"    outputs ",[188,1228,543],{"class":413},[188,1230,769],{"class":417},[188,1232,1233],{"class":197},"\"GET\"",[188,1235,575],{"class":417},[188,1237,675],{"class":413},[188,1239,487],{"class":197},[188,1241,491],{"class":490},[188,1243,1174],{"class":417},[188,1245,1246],{"class":197},"'Model'",[188,1248,1180],{"class":417},[188,1250,496],{"class":490},[188,1252,1253],{"class":197},"\u002Frange(address='A1:B3')\"",[188,1255,1256],{"class":417},").json()[",[188,1258,1196],{"class":197},[188,1260,824],{"class":417},[188,1262,1263,1266],{"class":81,"line":618},[188,1264,1265],{"class":466},"print",[188,1267,1268],{"class":417},"(outputs)\n",[10,1270,1271,1274,1275,1278,1279,1282],{},[185,1272,1273],{},"Recalculate"," computes formulas Excel considers dirty, which is enough almost always. ",[185,1276,1277],{},"Full"," recalculates every formula regardless of whether it looks\nchanged, and ",[185,1280,1281],{},"FullRebuild"," also rebuilds the dependency chain — the online equivalents of Ctrl+Alt+F9 and Ctrl+Shift+Alt+F9. Use the heavier levels when a\nmodel relies on volatile or user-defined behaviour that ordinary recalculation misses, not by default.",[29,1284,38,1290,38,1293,38,1296,38,1300,38,1305,38,1310,38,1315,38,1321,38,1328,38,1333,38,1336,38,1339,38,1342,38,1348,38,1352,38,1355,38,1358,38,1361,38,1367,38,1371],{"viewBox":1285,"role":32,"ariaLabelledBy":1286,"xmlns":36,"style":1289},"0 0 720 212",[1287,1288],"ses-calc-t","ses-calc-d","width:100%;max-width:720px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[40,1291,1292],{"id":1287},"Relative cost of the three calculation types",[44,1294,1295],{"id":1288},"Recalculate only computes formulas marked dirty and is cheapest. Full recalculates every formula. FullRebuild also rebuilds the dependency tree and is the most expensive.",[48,1297],{"x":50,"y":50,"width":1298,"height":1299,"fill":53},"720","212",[64,1301,1304],{"x":56,"y":1302,"style":1303},"30","font-size:12px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:start","relative calculation work on a large model",[48,1306],{"x":56,"y":310,"width":1307,"height":1308,"rx":1309,"fill":53,"stroke":85,"style":317},"170","28","6",[64,1311,1273],{"x":1312,"y":1313,"style":1314},"105","65","font-size:12px;font-weight:600;fill:var(--text,#172033);text-anchor:middle",[48,1316],{"x":1317,"y":1318,"width":1319,"height":1320,"rx":1309,"fill":357,"stroke":85,"style":317},"204","47","330","26",[48,1322],{"x":1323,"y":1324,"width":1325,"height":1326,"rx":1327,"fill":60,"stroke":61,"style":317},"205","48","64","24","5",[64,1329,1332],{"x":1330,"y":1313,"style":1331},"546","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:start","dirty formulas",[48,1334],{"x":56,"y":1335,"width":1307,"height":1308,"rx":1309,"fill":53,"stroke":85,"style":317},"90",[64,1337,1277],{"x":1312,"y":1338,"style":1314},"109",[48,1340],{"x":1317,"y":1341,"width":1319,"height":1320,"rx":1309,"fill":357,"stroke":85,"style":317},"91",[48,1343],{"x":1323,"y":1344,"width":1345,"height":1326,"rx":1327,"fill":1346,"stroke":1347,"style":317},"92","212.5","#fdefd8","var(--gold,#b4740a)",[64,1349,1351],{"x":1330,"y":1338,"style":1350},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:start","every formula",[48,1353],{"x":56,"y":1354,"width":1307,"height":1308,"rx":1309,"fill":53,"stroke":85,"style":317},"134",[64,1356,1281],{"x":1312,"y":1357,"style":1314},"153",[48,1359],{"x":1317,"y":1360,"width":1319,"height":1320,"rx":1309,"fill":357,"stroke":85,"style":317},"135",[48,1362],{"x":1323,"y":1363,"width":1364,"height":1326,"rx":1327,"fill":1365,"stroke":1366,"style":317},"136","311.5","#fee8f2","var(--accent,#f43f8f)",[64,1368,1370],{"x":1330,"y":1357,"style":1369},"font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:start","+ dependency tree",[64,1372,1375],{"x":1373,"y":1374,"style":170},"360","194","start with Recalculate; escalate only when results look stale",[173,1377,1379],{"id":1378},"step-4-run-what-if-scenarios-without-touching-the-file","Step 4 — Run what-if scenarios without touching the file",[10,1381,1382],{},"A non-persistent session turns a shared workbook into a calculation service. Write a scenario's inputs, recalculate, read the outputs, and let the session\nend: the file in SharePoint never changes, so the owner's model is never left with somebody's test assumptions in it.",[178,1384,1386],{"className":404,"code":1385,"language":406,"meta":183,"style":183},"import pandas as pd\n\nscenarios = pd.DataFrame({\n    \"name\": [\"base\", \"price rise\", \"downturn\"],\n    \"growth\": [0.05, 0.05, -0.02],\n    \"price\": [1200, 1320, 1150],\n    \"churn\": [0.18, 0.21, 0.25],\n})\n\nresults = []\nwith workbook_session(persist=False):\n    for row in scenarios.itertuples(index=False):\n        call(\"PATCH\", f\"{sheet('Inputs')}\u002Frange(address='B2:B4')\",\n             json={\"values\": [[row.growth], [row.price], [row.churn]]})\n        recalculate()\n        values = call(\"GET\", f\"{sheet('Model')}\u002Frange(address='A1:B3')\").json()[\"values\"]\n        results.append({\"scenario\": row.name, **{label: value for label, value in values[1:]}})\n\nprint(pd.DataFrame(results))\n",[185,1387,1388,1400,1404,1414,1438,1461,1482,1503,1508,1512,1522,1536,1559,1587,1601,1606,1641,1674,1678],{"__ignoreMap":183},[188,1389,1390,1392,1395,1397],{"class":81,"line":190},[188,1391,414],{"class":413},[188,1393,1394],{"class":417}," pandas ",[188,1396,943],{"class":413},[188,1398,1399],{"class":417}," pd\n",[188,1401,1402],{"class":81,"line":421},[188,1403,460],{"emptyLinePlaceholder":459},[188,1405,1406,1409,1411],{"class":81,"line":435},[188,1407,1408],{"class":417},"scenarios ",[188,1410,543],{"class":413},[188,1412,1413],{"class":417}," pd.DataFrame({\n",[188,1415,1416,1419,1422,1425,1427,1430,1432,1435],{"class":81,"line":448},[188,1417,1418],{"class":197},"    \"name\"",[188,1420,1421],{"class":417},": [",[188,1423,1424],{"class":197},"\"base\"",[188,1426,575],{"class":417},[188,1428,1429],{"class":197},"\"price rise\"",[188,1431,575],{"class":417},[188,1433,1434],{"class":197},"\"downturn\"",[188,1436,1437],{"class":417},"],\n",[188,1439,1440,1443,1445,1447,1449,1451,1453,1456,1459],{"class":81,"line":456},[188,1441,1442],{"class":197},"    \"growth\"",[188,1444,1421],{"class":417},[188,1446,1202],{"class":466},[188,1448,575],{"class":417},[188,1450,1202],{"class":466},[188,1452,575],{"class":417},[188,1454,1455],{"class":413},"-",[188,1457,1458],{"class":466},"0.02",[188,1460,1437],{"class":417},[188,1462,1463,1466,1468,1470,1472,1475,1477,1480],{"class":81,"line":463},[188,1464,1465],{"class":197},"    \"price\"",[188,1467,1421],{"class":417},[188,1469,1208],{"class":466},[188,1471,575],{"class":417},[188,1473,1474],{"class":466},"1320",[188,1476,575],{"class":417},[188,1478,1479],{"class":466},"1150",[188,1481,1437],{"class":417},[188,1483,1484,1487,1489,1491,1493,1496,1498,1501],{"class":81,"line":476},[188,1485,1486],{"class":197},"    \"churn\"",[188,1488,1421],{"class":417},[188,1490,1213],{"class":466},[188,1492,575],{"class":417},[188,1494,1495],{"class":466},"0.21",[188,1497,575],{"class":417},[188,1499,1500],{"class":466},"0.25",[188,1502,1437],{"class":417},[188,1504,1505],{"class":81,"line":532},[188,1506,1507],{"class":417},"})\n",[188,1509,1510],{"class":81,"line":537},[188,1511,460],{"emptyLinePlaceholder":459},[188,1513,1514,1517,1519],{"class":81,"line":549},[188,1515,1516],{"class":417},"results ",[188,1518,543],{"class":413},[188,1520,1521],{"class":417}," []\n",[188,1523,1524,1526,1528,1530,1532,1534],{"class":81,"line":584},[188,1525,926],{"class":413},[188,1527,929],{"class":417},[188,1529,932],{"class":629},[188,1531,543],{"class":413},[188,1533,937],{"class":466},[188,1535,758],{"class":417},[188,1537,1538,1541,1544,1547,1550,1553,1555,1557],{"class":81,"line":589},[188,1539,1540],{"class":413},"    for",[188,1542,1543],{"class":417}," row ",[188,1545,1546],{"class":413},"in",[188,1548,1549],{"class":417}," scenarios.itertuples(",[188,1551,1552],{"class":629},"index",[188,1554,543],{"class":413},[188,1556,937],{"class":466},[188,1558,758],{"class":417},[188,1560,1561,1564,1566,1568,1570,1572,1574,1576,1578,1580,1582,1584],{"class":81,"line":618},[188,1562,1563],{"class":417},"        call(",[188,1565,1163],{"class":197},[188,1567,575],{"class":417},[188,1569,675],{"class":413},[188,1571,487],{"class":197},[188,1573,491],{"class":490},[188,1575,1174],{"class":417},[188,1577,1177],{"class":197},[188,1579,1180],{"class":417},[188,1581,496],{"class":490},[188,1583,1185],{"class":197},[188,1585,1586],{"class":417},",\n",[188,1588,1589,1592,1594,1596,1598],{"class":81,"line":645},[188,1590,1591],{"class":629},"             json",[188,1593,543],{"class":413},[188,1595,491],{"class":417},[188,1597,1196],{"class":197},[188,1599,1600],{"class":417},": [[row.growth], [row.price], [row.churn]]})\n",[188,1602,1603],{"class":81,"line":663},[188,1604,1605],{"class":417},"        recalculate()\n",[188,1607,1608,1611,1613,1615,1617,1619,1621,1623,1625,1627,1629,1631,1633,1635,1637,1639],{"class":81,"line":724},[188,1609,1610],{"class":417},"        values ",[188,1612,543],{"class":413},[188,1614,769],{"class":417},[188,1616,1233],{"class":197},[188,1618,575],{"class":417},[188,1620,675],{"class":413},[188,1622,487],{"class":197},[188,1624,491],{"class":490},[188,1626,1174],{"class":417},[188,1628,1246],{"class":197},[188,1630,1180],{"class":417},[188,1632,496],{"class":490},[188,1634,1253],{"class":197},[188,1636,1256],{"class":417},[188,1638,1196],{"class":197},[188,1640,824],{"class":417},[188,1642,1643,1646,1649,1652,1654,1657,1660,1663,1665,1668,1671],{"class":81,"line":733},[188,1644,1645],{"class":417},"        results.append({",[188,1647,1648],{"class":197},"\"scenario\"",[188,1650,1651],{"class":417},": row.name, ",[188,1653,612],{"class":413},[188,1655,1656],{"class":417},"{label: value ",[188,1658,1659],{"class":413},"for",[188,1661,1662],{"class":417}," label, value ",[188,1664,1546],{"class":413},[188,1666,1667],{"class":417}," values[",[188,1669,1670],{"class":466},"1",[188,1672,1673],{"class":417},":]}})\n",[188,1675,1676],{"class":81,"line":738},[188,1677,460],{"emptyLinePlaceholder":459},[188,1679,1680,1682],{"class":81,"line":744},[188,1681,1265],{"class":466},[188,1683,1684],{"class":417},"(pd.DataFrame(results))\n",[10,1686,1687,1688,1692],{},"All three scenarios run inside one session, so the workbook is opened once, and each scenario's inputs simply overwrite the previous one's. Keep scenario runs\nsequential: the session holds one set of inputs at a time, and parallel requests would read each other's results. If the answers feed a report, write them with\nthe file-level tools in ",[23,1689,1691],{"href":1690},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002F","Building Excel Reports with XlsxWriter","\nrather than back into the model.",[173,1694,1696],{"id":1695},"step-5-handle-slow-session-creation","Step 5 — Handle slow session creation",[10,1698,1699,1700,1703,1704,1707,1708,1711,1712,1715],{},"Opening a large workbook can take long enough for a normal request to time out. Session creation supports Microsoft's long-running operation pattern: send\n",[185,1701,1702],{},"Prefer: respond-async",", and the service either answers immediately with ",[185,1705,1706],{},"201 Created"," or returns ",[185,1709,1710],{},"202 Accepted"," with a ",[185,1713,1714],{},"Location"," header to poll.",[178,1717,1719],{"className":404,"code":1718,"language":406,"meta":183,"style":183},"import time\n\ndef create_session_async(persist: bool, poll_seconds: int = 30, max_wait: int = 600) -> str:\n    response = call(\"POST\", f\"{BOOK}\u002FcreateSession\", json={\"persistChanges\": persist},\n                    headers={\"Prefer\": \"respond-async\"})\n    if response.status_code == 201:\n        return response.json()[\"id\"]\n    status_url = response.headers[\"Location\"]\n    waited = 0\n    while waited \u003C max_wait:\n        time.sleep(poll_seconds)\n        waited += poll_seconds\n        status = call(\"GET\", status_url).json()\n        if status[\"status\"] == \"succeeded\":\n            return call(\"GET\", status[\"resourceLocation\"]).json()[\"id\"]\n        if status[\"status\"] == \"failed\":\n            raise RuntimeError(f\"session creation failed: {status.get('error')}\")\n    raise TimeoutError(\"workbook did not open in time\")\n\nprint(create_session_async(persist=False))\n",[185,1720,1721,1728,1732,1770,1807,1826,1840,1852,1867,1877,1891,1896,1907,1921,1941,1963,1980,2010,2025,2029],{"__ignoreMap":183},[188,1722,1723,1725],{"class":81,"line":190},[188,1724,414],{"class":413},[188,1726,1727],{"class":417}," time\n",[188,1729,1730],{"class":81,"line":421},[188,1731,460],{"emptyLinePlaceholder":459},[188,1733,1734,1736,1739,1741,1743,1746,1749,1751,1754,1757,1759,1761,1764,1766,1768],{"class":81,"line":435},[188,1735,592],{"class":413},[188,1737,1738],{"class":595}," create_session_async",[188,1740,752],{"class":417},[188,1742,755],{"class":466},[188,1744,1745],{"class":417},", poll_seconds: ",[188,1747,1748],{"class":466},"int",[188,1750,470],{"class":413},[188,1752,1753],{"class":466}," 30",[188,1755,1756],{"class":417},", max_wait: ",[188,1758,1748],{"class":466},[188,1760,470],{"class":413},[188,1762,1763],{"class":466}," 600",[188,1765,1003],{"class":417},[188,1767,602],{"class":466},[188,1769,660],{"class":417},[188,1771,1772,1774,1776,1778,1780,1782,1784,1786,1788,1790,1792,1794,1796,1798,1800,1802,1804],{"class":81,"line":448},[188,1773,621],{"class":417},[188,1775,543],{"class":413},[188,1777,769],{"class":417},[188,1779,772],{"class":197},[188,1781,575],{"class":417},[188,1783,675],{"class":413},[188,1785,487],{"class":197},[188,1787,491],{"class":490},[188,1789,479],{"class":466},[188,1791,496],{"class":490},[188,1793,787],{"class":197},[188,1795,575],{"class":417},[188,1797,792],{"class":629},[188,1799,543],{"class":413},[188,1801,491],{"class":417},[188,1803,799],{"class":197},[188,1805,1806],{"class":417},": persist},\n",[188,1808,1809,1812,1814,1816,1819,1821,1824],{"class":81,"line":456},[188,1810,1811],{"class":629},"                    headers",[188,1813,543],{"class":413},[188,1815,491],{"class":417},[188,1817,1818],{"class":197},"\"Prefer\"",[188,1820,705],{"class":417},[188,1822,1823],{"class":197},"\"respond-async\"",[188,1825,1507],{"class":417},[188,1827,1828,1830,1832,1835,1838],{"class":81,"line":463},[188,1829,648],{"class":413},[188,1831,651],{"class":417},[188,1833,1834],{"class":413},"==",[188,1836,1837],{"class":466}," 201",[188,1839,660],{"class":417},[188,1841,1842,1845,1848,1850],{"class":81,"line":476},[188,1843,1844],{"class":413},"        return",[188,1846,1847],{"class":417}," response.json()[",[188,1849,821],{"class":197},[188,1851,824],{"class":417},[188,1853,1854,1857,1859,1862,1865],{"class":81,"line":532},[188,1855,1856],{"class":417},"    status_url ",[188,1858,543],{"class":413},[188,1860,1861],{"class":417}," response.headers[",[188,1863,1864],{"class":197},"\"Location\"",[188,1866,824],{"class":417},[188,1868,1869,1872,1874],{"class":81,"line":537},[188,1870,1871],{"class":417},"    waited ",[188,1873,543],{"class":413},[188,1875,1876],{"class":466}," 0\n",[188,1878,1879,1882,1885,1888],{"class":81,"line":549},[188,1880,1881],{"class":413},"    while",[188,1883,1884],{"class":417}," waited ",[188,1886,1887],{"class":413},"\u003C",[188,1889,1890],{"class":417}," max_wait:\n",[188,1892,1893],{"class":81,"line":584},[188,1894,1895],{"class":417},"        time.sleep(poll_seconds)\n",[188,1897,1898,1901,1904],{"class":81,"line":589},[188,1899,1900],{"class":417},"        waited ",[188,1902,1903],{"class":413},"+=",[188,1905,1906],{"class":417}," poll_seconds\n",[188,1908,1909,1912,1914,1916,1918],{"class":81,"line":618},[188,1910,1911],{"class":417},"        status ",[188,1913,543],{"class":413},[188,1915,769],{"class":417},[188,1917,1233],{"class":197},[188,1919,1920],{"class":417},", status_url).json()\n",[188,1922,1923,1926,1929,1932,1934,1936,1939],{"class":81,"line":645},[188,1924,1925],{"class":413},"        if",[188,1927,1928],{"class":417}," status[",[188,1930,1931],{"class":197},"\"status\"",[188,1933,558],{"class":417},[188,1935,1834],{"class":413},[188,1937,1938],{"class":197}," \"succeeded\"",[188,1940,660],{"class":417},[188,1942,1943,1946,1948,1950,1953,1956,1959,1961],{"class":81,"line":663},[188,1944,1945],{"class":413},"            return",[188,1947,769],{"class":417},[188,1949,1233],{"class":197},[188,1951,1952],{"class":417},", status[",[188,1954,1955],{"class":197},"\"resourceLocation\"",[188,1957,1958],{"class":417},"]).json()[",[188,1960,821],{"class":197},[188,1962,824],{"class":417},[188,1964,1965,1967,1969,1971,1973,1975,1978],{"class":81,"line":724},[188,1966,1925],{"class":413},[188,1968,1928],{"class":417},[188,1970,1931],{"class":197},[188,1972,558],{"class":417},[188,1974,1834],{"class":413},[188,1976,1977],{"class":197}," \"failed\"",[188,1979,660],{"class":417},[188,1981,1982,1985,1987,1989,1991,1994,1996,1999,2002,2004,2006,2008],{"class":81,"line":733},[188,1983,1984],{"class":413},"            raise",[188,1986,669],{"class":466},[188,1988,672],{"class":417},[188,1990,675],{"class":413},[188,1992,1993],{"class":197},"\"session creation failed: ",[188,1995,491],{"class":490},[188,1997,1998],{"class":417},"status.get(",[188,2000,2001],{"class":197},"'error'",[188,2003,1180],{"class":417},[188,2005,496],{"class":490},[188,2007,487],{"class":197},[188,2009,581],{"class":417},[188,2011,2012,2015,2018,2020,2023],{"class":81,"line":738},[188,2013,2014],{"class":413},"    raise",[188,2016,2017],{"class":466}," TimeoutError",[188,2019,672],{"class":417},[188,2021,2022],{"class":197},"\"workbook did not open in time\"",[188,2024,581],{"class":417},[188,2026,2027],{"class":81,"line":744},[188,2028,460],{"emptyLinePlaceholder":459},[188,2030,2031,2033,2036,2038,2040,2042],{"class":81,"line":761},[188,2032,1265],{"class":466},[188,2034,2035],{"class":417},"(create_session_async(",[188,2037,932],{"class":629},[188,2039,543],{"class":413},[188,2041,937],{"class":466},[188,2043,2044],{"class":417},"))\n",[10,2046,2047,2048,2051,2052,2055,2056,2059,2060,2063,2064,250],{},"Microsoft suggests polling roughly every 30 seconds and never waiting more than four minutes between polls. The operation's status moves through ",[185,2049,2050],{},"notStarted","\nand ",[185,2053,2054],{},"running"," to ",[185,2057,2058],{},"succeeded"," or ",[185,2061,2062],{},"failed",", and a successful operation points to the session information through ",[185,2065,2066],{},"resourceLocation",[173,2068,2070],{"id":2069},"step-6-keep-a-session-alive-through-a-long-job","Step 6 — Keep a session alive through a long job",[10,2072,2073,2074,2077],{},"A job that interleaves workbook calls with slow work of its own — a database query between scenarios, a large pandas transform before writing results — can\noutlast the session's idle timeout. Rather than guessing how long the pause will be, refresh the session whenever enough time has passed since the last\nworkbook request. ",[185,2075,2076],{},"refreshSession"," takes the same session header and has no body to speak of.",[178,2079,2081],{"className":404,"code":2080,"language":406,"meta":183,"style":183},"REFRESH_AFTER = 120          # seconds; comfortably inside the idle timeout\n\nclass SessionKeeper:\n    def __init__(self):\n        self.last = time.monotonic()\n\n    def touch(self) -> None:\n        self.last = time.monotonic()\n\n    def keep_alive(self) -> None:\n        if time.monotonic() - self.last > REFRESH_AFTER:\n            call(\"POST\", f\"{BOOK}\u002FrefreshSession\", json={})\n            self.touch()\n\nkeeper = SessionKeeper()\nwith workbook_session(persist=True):\n    for batch in range(3):\n        keeper.keep_alive()                                   # before each workbook call\n        call(\"GET\", f\"{sheet('Model')}\u002Frange(address='A1:B3')\")\n        keeper.touch()\n        # ... slow local work between workbook calls ...\nprint(\"finished with the session still valid\")\n",[185,2082,2083,2096,2100,2110,2121,2134,2138,2152,2162,2166,2179,2201,2230,2238,2242,2252,2266,2285,2293,2319,2324,2329],{"__ignoreMap":183},[188,2084,2085,2088,2090,2093],{"class":81,"line":190},[188,2086,2087],{"class":466},"REFRESH_AFTER",[188,2089,470],{"class":413},[188,2091,2092],{"class":466}," 120",[188,2094,2095],{"class":1087},"          # seconds; comfortably inside the idle timeout\n",[188,2097,2098],{"class":81,"line":421},[188,2099,460],{"emptyLinePlaceholder":459},[188,2101,2102,2105,2108],{"class":81,"line":435},[188,2103,2104],{"class":413},"class",[188,2106,2107],{"class":193}," SessionKeeper",[188,2109,660],{"class":417},[188,2111,2112,2115,2118],{"class":81,"line":448},[188,2113,2114],{"class":413},"    def",[188,2116,2117],{"class":466}," __init__",[188,2119,2120],{"class":417},"(self):\n",[188,2122,2123,2126,2129,2131],{"class":81,"line":456},[188,2124,2125],{"class":466},"        self",[188,2127,2128],{"class":417},".last ",[188,2130,543],{"class":413},[188,2132,2133],{"class":417}," time.monotonic()\n",[188,2135,2136],{"class":81,"line":463},[188,2137,460],{"emptyLinePlaceholder":459},[188,2139,2140,2142,2145,2148,2150],{"class":81,"line":476},[188,2141,2114],{"class":413},[188,2143,2144],{"class":595}," touch",[188,2146,2147],{"class":417},"(self) -> ",[188,2149,913],{"class":466},[188,2151,660],{"class":417},[188,2153,2154,2156,2158,2160],{"class":81,"line":532},[188,2155,2125],{"class":466},[188,2157,2128],{"class":417},[188,2159,543],{"class":413},[188,2161,2133],{"class":417},[188,2163,2164],{"class":81,"line":537},[188,2165,460],{"emptyLinePlaceholder":459},[188,2167,2168,2170,2173,2175,2177],{"class":81,"line":549},[188,2169,2114],{"class":413},[188,2171,2172],{"class":595}," keep_alive",[188,2174,2147],{"class":417},[188,2176,913],{"class":466},[188,2178,660],{"class":417},[188,2180,2181,2183,2186,2188,2191,2193,2196,2199],{"class":81,"line":584},[188,2182,1925],{"class":413},[188,2184,2185],{"class":417}," time.monotonic() ",[188,2187,1455],{"class":413},[188,2189,2190],{"class":466}," self",[188,2192,2128],{"class":417},[188,2194,2195],{"class":413},">",[188,2197,2198],{"class":466}," REFRESH_AFTER",[188,2200,660],{"class":417},[188,2202,2203,2205,2207,2209,2211,2213,2215,2217,2219,2222,2224,2226,2228],{"class":81,"line":589},[188,2204,866],{"class":417},[188,2206,772],{"class":197},[188,2208,575],{"class":417},[188,2210,675],{"class":413},[188,2212,487],{"class":197},[188,2214,491],{"class":490},[188,2216,479],{"class":466},[188,2218,496],{"class":490},[188,2220,2221],{"class":197},"\u002FrefreshSession\"",[188,2223,575],{"class":417},[188,2225,792],{"class":629},[188,2227,543],{"class":413},[188,2229,892],{"class":417},[188,2231,2232,2235],{"class":81,"line":618},[188,2233,2234],{"class":466},"            self",[188,2236,2237],{"class":417},".touch()\n",[188,2239,2240],{"class":81,"line":645},[188,2241,460],{"emptyLinePlaceholder":459},[188,2243,2244,2247,2249],{"class":81,"line":663},[188,2245,2246],{"class":417},"keeper ",[188,2248,543],{"class":413},[188,2250,2251],{"class":417}," SessionKeeper()\n",[188,2253,2254,2256,2258,2260,2262,2264],{"class":81,"line":724},[188,2255,926],{"class":413},[188,2257,929],{"class":417},[188,2259,932],{"class":629},[188,2261,543],{"class":413},[188,2263,1154],{"class":466},[188,2265,758],{"class":417},[188,2267,2268,2270,2273,2275,2278,2280,2283],{"class":81,"line":733},[188,2269,1540],{"class":413},[188,2271,2272],{"class":417}," batch ",[188,2274,1546],{"class":413},[188,2276,2277],{"class":466}," range",[188,2279,672],{"class":417},[188,2281,2282],{"class":466},"3",[188,2284,758],{"class":417},[188,2286,2287,2290],{"class":81,"line":738},[188,2288,2289],{"class":417},"        keeper.keep_alive()                                   ",[188,2291,2292],{"class":1087},"# before each workbook call\n",[188,2294,2295,2297,2299,2301,2303,2305,2307,2309,2311,2313,2315,2317],{"class":81,"line":744},[188,2296,1563],{"class":417},[188,2298,1233],{"class":197},[188,2300,575],{"class":417},[188,2302,675],{"class":413},[188,2304,487],{"class":197},[188,2306,491],{"class":490},[188,2308,1174],{"class":417},[188,2310,1246],{"class":197},[188,2312,1180],{"class":417},[188,2314,496],{"class":490},[188,2316,1253],{"class":197},[188,2318,581],{"class":417},[188,2320,2321],{"class":81,"line":761},[188,2322,2323],{"class":417},"        keeper.touch()\n",[188,2325,2326],{"class":81,"line":805},[188,2327,2328],{"class":1087},"        # ... slow local work between workbook calls ...\n",[188,2330,2331,2333,2335,2338],{"class":81,"line":827},[188,2332,1265],{"class":466},[188,2334,672],{"class":417},[188,2336,2337],{"class":197},"\"finished with the session still valid\"",[188,2339,581],{"class":417},[10,2341,2342,2343,2346,2347,2349],{},"Calling ",[185,2344,2345],{},"keep_alive"," before each workbook request, rather than on a timer thread, keeps the logic single-threaded and makes the refresh happen exactly when it is\nneeded. If a refresh itself fails because the session has already expired, the context manager's ",[185,2348,392],{}," will fail too; catch that, create a new session and\nrewrite the inputs, which is safe because a non-persistent session never saved anything in the first place.",[173,2351,2353],{"id":2352},"common-pitfalls","Common pitfalls",[2355,2356,2357,2373],"table",{},[2358,2359,2360],"thead",{},[2361,2362,2363,2367,2370],"tr",{},[2364,2365,2366],"th",{},"Symptom",[2364,2368,2369],{},"Cause",[2364,2371,2372],{},"Fix",[2374,2375,2376,2392,2409,2420,2433,2446],"tbody",{},[2361,2377,2378,2382,2385],{},[2379,2380,2381],"td",{},"Outputs unchanged after writing inputs",[2379,2383,2384],{},"Workbook in manual calculation mode",[2379,2386,2387,2388,2391],{},"Call ",[185,2389,2390],{},"application\u002Fcalculate"," before reading",[2361,2393,2394,2397,2402],{},[2379,2395,2396],{},"What-if inputs appear in the shared file",[2379,2398,2399,2400],{},"Session created with ",[185,2401,268],{},[2379,2403,2404,2405,2408],{},"Use ",[185,2406,2407],{},"false"," for scenarios",[2361,2410,2411,2414,2417],{},[2379,2412,2413],{},"Calculate fails on a personal OneDrive",[2379,2415,2416],{},"Not supported for personal accounts",[2379,2418,2419],{},"Use a work or school account",[2361,2421,2422,2425,2428],{},[2379,2423,2424],{},"Later calls fail with a session error",[2379,2426,2427],{},"Session expired during a pause",[2379,2429,2430,2432],{},[185,2431,2076],{},", or create a new session",[2361,2434,2435,2438,2441],{},[2379,2436,2437],{},"Timeouts creating the session",[2379,2439,2440],{},"Large workbook takes long to open",[2379,2442,2404,2443,2445],{},[185,2444,1702],{}," and poll",[2361,2447,2448,2451,2454],{},[2379,2449,2450],{},"Scenarios return mixed results",[2379,2452,2453],{},"Parallel requests in one session",[2379,2455,2456],{},"Run scenarios sequentially",[173,2458,2460],{"id":2459},"performance-and-scale-notes","Performance and scale notes",[10,2462,2463,2464,2466,2467,2470,2471,250],{},"A session removes the cost of opening the workbook on every call, which is the dominant cost for large files, so a ten-call job in a session is usually several\ntimes faster than the same calls sessionless. Calculation time depends entirely on the model; measure one ",[185,2465,1273],{}," and budget scenarios from that. Microsoft\nrecommends sequential rather than concurrent requests against a single workbook, and heavy use brings throttling responses with a ",[185,2468,2469],{},"Retry-After"," header — honour it,\nas in ",[23,2472,2474],{"href":2473},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fappend-rows-to-an-excel-online-table-with-microsoft-graph\u002F","Append Rows to an Excel Online Table with Microsoft Graph",[173,2476,2478],{"id":2477},"conclusion","Conclusion",[10,2480,2481,2482,2484,2485,2487,2488,2490,2491,2493,2494,2496],{},"Wrap any multi-call workbook job in a session, closed reliably by a context manager. Choose ",[185,2483,268],{}," when the job should update the file and ",[185,2486,2407],{},"\nwhen it only needs answers — the non-persistent session is what makes a shared Excel model safe to use as a calculation service. Call ",[185,2489,2390],{}," with\n",[185,2492,1273],{}," before reading outputs from a manually calculated workbook, escalate to ",[185,2495,1277],{}," only when results look stale, and use the asynchronous session pattern\nfor workbooks that are slow to open.",[173,2498,2500],{"id":2499},"frequently-asked-questions","Frequently asked questions",[10,2502,2503,2506],{},[14,2504,2505],{},"Do I need a session to call calculate?","\nNo, but without one the workbook is opened, recalculated, saved and closed in a single request, and any inputs you wrote in an earlier sessionless call have already been saved. Sessions make the sequence predictable.",[10,2508,2509,2512],{},[14,2510,2511],{},"How long does a workbook session last?","\nSessions expire after a few minutes without activity. Call refreshSession to keep one alive during a pause, or create a new session when work resumes.",[10,2514,2515,2518],{},[14,2516,2517],{},"Will people with the file open see my what-if inputs?","\nNot with a non-persistent session, whose changes are never saved. With a persistent session they see changes as they are written, just like co-authoring.",[10,2520,2521,2524],{},[14,2522,2523],{},"Can I evaluate a single Excel function without a model?","\nYes. The workbook functions endpoints evaluate individual worksheet functions such as VLOOKUP or PMT, though for most calculations pandas is simpler once the data is in Python.",[173,2526,2528],{"id":2527},"related","Related",[209,2530,2531,2537,2544,2551,2558],{},[212,2532,2533,2534,2536],{},"Up one level: ",[23,2535,26],{"href":25}," — the full workbook API workflow.",[212,2538,2539,2543],{},[23,2540,2542],{"href":2541},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fread-and-write-excel-online-ranges-with-microsoft-graph\u002F","Read and Write Excel Online Ranges with Microsoft Graph"," — the range reads and writes used in each scenario.",[212,2545,2546,2550],{},[23,2547,2549],{"href":2548},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Frecalculate-excel-formulas-without-excel-in-python\u002F","Recalculate Excel Formulas Without Excel in Python"," — local alternatives for files on disk.",[212,2552,2553,2557],{},[23,2554,2556],{"href":2555},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Frefresh-excel-data-connections-and-recalculate-with-win32com\u002F","Refresh Excel Data Connections and Recalculate with win32com"," — the desktop Excel equivalent.",[212,2559,2560,2562],{},[23,2561,226],{"href":225}," — the delegated token these calls require.",[2564,2565,2566],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}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);}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 .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":183,"searchDepth":421,"depth":421,"links":2568},[2569,2570,2571,2572,2573,2574,2575,2576,2577,2578,2579,2580],{"id":175,"depth":421,"text":176},{"id":253,"depth":421,"text":254},{"id":385,"depth":421,"text":386},{"id":970,"depth":421,"text":971},{"id":1378,"depth":421,"text":1379},{"id":1695,"depth":421,"text":1696},{"id":2069,"depth":421,"text":2070},{"id":2352,"depth":421,"text":2353},{"id":2459,"depth":421,"text":2460},{"id":2477,"depth":421,"text":2478},{"id":2499,"depth":421,"text":2500},{"id":2527,"depth":421,"text":2528},"2026-09-17","Use workbook sessions to make multi-call Graph jobs fast and consistent, force a recalculation before reading results, run what-if scenarios that are never saved, and handle slow session creation.","md",[2585,2587,2589,2591],{"q":2505,"a":2586},"No, but without one the workbook is opened, recalculated, saved and closed in a single request, and any inputs you wrote in an earlier sessionless call have already been saved. Sessions make the sequence predictable.",{"q":2511,"a":2588},"Sessions expire after a few minutes without activity. Call refreshSession to keep one alive during a pause, or create a new session when work resumes.",{"q":2517,"a":2590},"Not with a non-persistent session, whose changes are never saved. With a persistent session they see changes as they are written, just like co-authoring.",{"q":2523,"a":2592},"Yes. The workbook functions endpoints evaluate individual worksheet functions such as VLOOKUP or PMT, though for most calculations pandas is simpler once the data is in Python.","recalculate excel online workbooks with graph sessions",null,{"breadcrumb":2596},[2597,2600,2603],{"name":2598,"item":2599},"Home","\u002F",{"name":2601,"item":2602},"Automating Reporting Workflows","\u002Fautomating-reporting-workflows\u002F",{"name":26,"item":25},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frecalculate-excel-online-workbooks-with-graph-sessions",{"title":2606,"description":2607},"Recalculate Excel Online Workbooks with Microsoft Graph Sessions","Create persistent and non-persistent Excel workbook sessions with Microsoft Graph in Python, call application\u002Fcalculate, run what-if models without saving, and poll long-running session creation.","recalculate-excel-online-workbooks-with-graph-sessions","automating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frecalculate-excel-online-workbooks-with-graph-sessions\u002Findex",[2611,2612],"excel-online","excel-formulas","how-to","6GvJvcpYwFSwI1xSraOa7Da6yDN7HbSNXRok1K7pKT0",[2616,2619],{"title":2542,"path":2617,"stem":2618,"children":-1},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fread-and-write-excel-online-ranges-with-microsoft-graph","automating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fread-and-write-excel-online-ranges-with-microsoft-graph\u002Findex",{"title":2620,"path":2621,"stem":2622,"children":-1},"Run a Report When an Excel File Changes in OneDrive","\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frun-a-report-when-an-excel-file-changes-in-onedrive","automating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frun-a-report-when-an-excel-file-changes-in-onedrive\u002Findex",[2624,2626],{"path":2625,"title":26},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph",{"path":2627,"title":2628},"\u002Fautomating-reporting-workflows","Automating Reporting Workflows with Python and Excel",[],[2631,2634,2637,2640,2642,2644],{"path":2632,"title":2633,"kind":2613},"\u002Fautomating-reporting-workflows\u002Femailing-excel-reports-with-smtplib\u002Fsend-an-excel-report-with-microsoft-graph-api","Send an Excel Report with the Microsoft Graph API",{"path":2635,"title":2636,"kind":2613},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fadd-a-chart-to-an-excel-online-workbook-with-microsoft-graph","Add a Chart to an Excel Online Workbook with Microsoft Graph",{"path":2638,"title":2639,"kind":2613},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fadd-rename-and-delete-worksheets-with-microsoft-graph","Add, Rename and Delete Worksheets with Microsoft Graph",{"path":2641,"title":2474,"kind":2613},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fappend-rows-to-an-excel-online-table-with-microsoft-graph",{"path":2643,"title":226,"kind":2613},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fauthenticate-to-microsoft-graph-with-msal-for-excel-automation",{"path":2645,"title":2646,"kind":2613},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcall-excel-worksheet-functions-through-microsoft-graph","Call Excel Worksheet Functions Through Microsoft Graph",1790972119076]