Flow Studio Blog

Monitor a Webpage in Power Automate Without Premium: The IMPORTHTML Trick

Written by Catherine Han | Apr 23, 2026 2:33:53 PM

How a Reddit question became a one-hour exercise in knowing when to pivot.

A community member asked on r/PowerAutomate:

Is there a reliable way to monitor a webpage section (like a table) using only standard connectors? I want an email whenever a new SQL Server 2025 GDR build is published on this Microsoft Learn page. No premium, no SSMS, converting to RSS couldn't isolate the GDR section reliably.

So I handed the problem to my agent — Claude Code + Flow Studio MCP — and gave it the brief: standard connectors only, which rules out the HTTP action that would normally solve this in ten minutes.

Here's what the working solution looks like, and more interestingly, the two wrong turns the agent took to get there.

Attempt 1 — Office Scripts fetch() (blocked by CORS)

The idea: Excel Online (Business) is a standard connector. Its "Run script" action can execute Office Scripts, and Office Scripts can call fetch(). So the plan was:

  1. Upload an .xlsx to SharePoint via Graph API (as a service principal)
  2. Seed a State sheet with the 5 existing KB numbers so the first run wouldn't email all 5
  3. Add an Office Script that fetches the Learn page, parses the GDR table, returns only new KBs
  4. PA flow: Recurrence → Run script → condition → email

Graph did the file creation cleanly. Seeding the State sheet via the workbook range API worked. Then the agent tried to upload the .osts Office Script file via Graph too — the write succeeded, but the script never appeared in Excel's Automate pane. Office Scripts aren't plain files; they're registered through Excel's UI. A minute of Excel Online clicks got the real script saved.

Then the first Run returned Failed to fetch.

The agent's first instinct was the tenant admin toggle ("Allow Office Scripts to call external APIs"). It chased that for a round-trip, found the setting wasn't even visible in this tenant's Org settings, invented a Graph endpoint that didn't exist, and generally lost time. Classic agent failure mode, worth naming so you can spot it in your own agent-driven work.

The 30-second diagnostic that actually worked: replace the url line in the script with https://api.github.com/zen and Run again. The script ran fine — the "GDR section anchor not found" check the agent had thrown fired, because /zen returns a fortune-cookie quote, not Learn HTML.

So fetch works. Just not for learn.microsoft.com.

The real blocker: CORS. The Office Scripts fetch() runtime enforces CORS. api.github.com sends Access-Control-Allow-Origin: *. Microsoft Learn does not, even for callers from other Microsoft properties. No admin setting overrides this.

There was a middle option we nearly took: Microsoft Learn docs are open-sourced. Click "Edit" on any Learn page and it points you to the source Markdown on GitHub. raw.githubusercontent.com does send permissive CORS. The GDR build-versions page lives at raw.githubusercontent.com/MicrosoftDocs/SupportArticles-docs/main/support/sql/releases/sqlserver-2025/build-versions.md. Markdown is easier to parse than rendered HTML anyway. This would have worked.

But I pulled the plug on the whole Office-Scripts-plus-SharePoint-plus-admin-toggles-plus-registration-dance — too fragile for what should be a simple thing.

Attempt 2 — Google Sheets IMPORTHTML

One formula replaces everything:

=IMPORTHTML("https://learn.microsoft.com/en-us/troubleshoot/sql/releases/sqlserver-2025/build-versions","table",2)



IMPORTHTML
runs server-side in Google's infrastructure. No CORS. No admin toggle. The formula spills the GDR table across the sheet and Google auto-refreshes it roughly hourly.

The Google Sheets connector in Power Automate is Standard tier — still satisfies the Reddit constraint.

Sheet structure:

  • Live tab — cell A1 holds the formula
  • State tab — single column KB, seeded with the 5 existing KB numbers

Flow, all standard connectors:

  1. Recurrence — daily
  2. Google Sheets → Get rows from Live
  3. Google Sheets → Get rows from State
  4. Select — map State rows to a flat array of KB strings
  5. Filter array — keep Live rows whose KB is not in that array
  6. Condition length > 0:
    • Select → build <tr> HTML strings from new rows
    • Office 365 Outlook → Send email with an HTML table
    • For each new row → Google Sheets → Insert row into State

The agent deployed the whole thing via Flow Studio MCP's update_live_flow, then flipped the trigger from Button to Recurrence (Button triggers can't be invoked via API in our tenant; Recurrence supports "Test → Manually" in the UI and resubmit via MCP).

The _x0020_ encoding bug that cost a run

First manual test fired the flow. Email arrived with 5 rows… all empty cells. Filter found every row "new" even though State already had all 5 KBs.

Action outputs told the story. The Google Sheets connector encodes column headers in its JSON response:

Header in sheet Property name in JSON
GDR name GDR_x0020_name
Knowledge Base number Knowledge_x0020_Base_x0020_number
SQL Server (sqlservr.exe) file version SQL_x0020_Server_x0020_(sqlservr_x002e_exe)_x0020_file_x0020_version

Space → _x0020_, dot → _x002e_, every non-alphanumeric character gets the _xHHHH_ Unicode encoding.

The PA designer hides this — it displays and accepts the pretty names in the UI, then serializes to the encoded form under the hood. Writing flow JSON directly (via MCP, ALM tooling, or manual definition editing), you must use the encoded form. item()?['Knowledge Base number'] doesn't throw — it returns null silently, and the whole flow cascades downhill:

  • contains(array, null) → always false → filter lets every row through
  • concat('<td>', null, '</td>') → empty cells
  • Insert row with null values → Sheets silently no-ops (creates blank rows)

All actions return Succeeded the whole time. Nothing in the run history looks wrong. This is exactly where Flow Studio MCP's get_live_flow_run_action_outputs earns its keep — without action-level input/output inspection, a silent-null bug like this is near-invisible. The agent pulled each action's input/output JSON, spotted the null in the filter, and traced it back to the column name.

Fix: item()?['Knowledge_x0020_Base_x0020_number']. The agent redeployed, resubmitted, and the email arrived with one populated row:

And State got the KB appended. Done.

Fragility — accepting the trade-offs

Both this and the GitHub-raw-markdown path are fragile in the same way:

  • IMPORTHTML("...", "table", 2) breaks if Microsoft adds a new table above the GDR one on the Learn page
  • Column-name references break if the table headers are restructured
  • IMPORTHTML's refresh cadence is Google-controlled; combining it with a PA Recurrence schedule stacks two polling layers

For a "tell me when a KB ships" use case — where a day of latency is fine and a page restructure means reopening this post — that's an acceptable trade. For anything time-critical or reliability-sensitive, the right answer is asking Microsoft for a structured feed or paying for the HTTP connector.

Three takeaways

  1. Office Scripts fetch() enforces CORS. 30-second diagnostic: swap in https://api.github.com/zen. If that works but your real URL fails, it's CORS, and no admin toggle will save you.
  2. Microsoft Learn docs are open-sourced on GitHub. When a Learn URL is CORS-blocked, the raw .md source typically isn't. Click "Edit" on any Learn page to find the source path.
  3. Google Sheets + Excel Online connectors encode column names in JSON. The designer hides it; hand-written flow JSON must use _x0020_ / _x002e_ / etc. Silent nulls are the failure mode — when your flow "succeeds" but outputs are blank, check encoding first.

The whole build + two-pivot debug cycle ran end-to-end through Flow Studio MCP: list_live_flows, get_live_flow, update_live_flow, trigger_live_flow, get_live_flow_runs, get_live_flow_run_action_outputs. The agent never needed me to open Power Automate designer. That's the sell — give your agent the MCP tool surface, give it a constraint, and let it work through its own dead-ends until the flow actually runs.

Fair warning if you build your own: it'll break the day Microsoft restructures the Learn page. That's the accepting-the-trade-off part.

Want to try Flow Studio MCP? Free plan includes 100 API calls, no credit card required.

Works with Copilot, Claude, and any MCP-compatible agent.

Catherine Han, Flow Studio