Scraping a JavaScript-Rendered Page Into Google Sheets
Back to blog
scraping·August 26, 2026·8 min read·By Yehonatan Saadia

Scraping a JavaScript-Rendered Page Into Google Sheets

IMPORTHTML returns nothing and IMPORTXML returns #N/A because the data is not in the HTML - it arrives later by JavaScript. Here are the four ways to get it into a Sheet anyway, ranked by how long each one keeps working.

Key takeaways

  • Google Sheets import functions fetch raw HTML and never run JavaScript. If the data is not in "view source", no formula will ever find it.
  • Before reaching for a browser, open the network tab. Most dynamic pages fetch their data from a JSON endpoint you can call directly - which is faster, cheaper and far more stable than rendering.
  • Apps Script can fetch, but it cannot render JavaScript either. It solves authentication and scheduling, not the rendering problem.
  • Headless browser rendering is the last resort and the most expensive to keep alive. Use it only when there is genuinely no data endpoint behind the page.

You paste a URL into IMPORTHTML, and you get an empty cell. You try IMPORTXML with an XPath you copied from the browser, and you get #N/A. The page clearly has a table on it - you are looking at it.

The reason is simple. Sheets fetches the raw HTML document that the server returns. Modern pages frequently return an almost empty shell and then fill it in with JavaScript once the browser runs. Sheets never runs that JavaScript, so it sees the shell.

Confirm the Diagnosis First

Open the page, view source (not the inspector - actual view-source), and search for a value you can see on screen. If it is not there, the page is client-rendered and no Sheets formula will ever work on it. If it is there, your XPath is wrong and the problem is much simpler than you think.

This takes thirty seconds and determines everything that follows.

Option 1: Find the Data Endpoint (Try This First)

If JavaScript is putting data on the page, it got that data from somewhere - almost always a request to a JSON endpoint. That endpoint is usually far easier to work with than the page.

Open the browser developer tools, go to the Network tab, filter to Fetch/XHR, and reload the page. Look through the responses for the one containing your data. When you find it, you have a URL that returns clean structured JSON, with no HTML parsing and no rendering required.

This is better than rendering on every dimension. It is faster, it costs almost nothing to run, and it is far more stable - a redesign that changes every CSS class on the page often leaves the underlying data endpoint untouched.

Once you have the endpoint, a small Apps Script function can fetch it, pull out the fields you need, and write them into the sheet on a schedule. That is usually thirty lines of code and it will outlive several redesigns.

Option 2: Apps Script Fetch

Apps Script's URL fetch is more capable than the import formulas - you can set headers, handle authentication, follow a login, parse JSON, and schedule runs with a time-based trigger.

What it cannot do is execute JavaScript. It fetches, exactly like the formulas do. So Apps Script is the right tool when you have found a data endpoint, or when the page is server-rendered but needs headers or credentials. It is not a solution to the rendering problem itself.

Option 3: A Rendering Service

When there is genuinely no accessible data endpoint - the data arrives through a mechanism you cannot replicate, or it is assembled from several sources client-side - you need something that actually runs a browser.

The practical pattern is to put the browser outside the spreadsheet. A small service, running wherever you already have somewhere to run things, loads the page in a headless browser, extracts what you need, and either writes to the sheet through the Sheets API or exposes a simple endpoint that Apps Script calls.

Be clear-eyed about the cost. Headless browsers are slow relative to a fetch, they consume real memory, and they are the most fragile part of any collection system - they break when the page changes, when a consent dialog appears, and when the site adds bot protection. This is a real project with real maintenance, not a formula.

Option 4: Reconsider the Requirement

Worth asking before building anything: does the site offer an official API, an export, or an RSS feed? Is there a public dataset with the same information? Does the data actually need to be in a spreadsheet, or is the spreadsheet just where reporting currently happens?

A surprising number of dynamic-page scraping requests dissolve when someone checks whether the source publishes the data another way.

Comparison

ApproachHandles JSEffortStability
IMPORTHTML / IMPORTXMLNoMinutesLow
Apps Script fetch on a data endpointNot neededHoursHigh
Apps Script fetch on rendered HTMLNoHoursMedium
External headless browser serviceYesDays to weeksLow to medium
Official API or exportNot neededHoursHighest

Practical Notes

  • Cache aggressively. If the underlying data changes daily, do not fetch it every time the sheet recalculates. Write to a hidden sheet on a schedule and have your visible formulas read from there.
  • Write a timestamp. Every automated write should stamp when it ran. A sheet showing stale data with no indication of staleness is how wrong decisions get made confidently.
  • Handle the empty case explicitly. If the fetch returns nothing, do not overwrite the previous values with blanks. Keep the last good data and flag the failure.
  • Rate limit. A trigger running every five minutes against someone's site is both rude and a good way to get blocked.
  • Check the terms. The legal position varies by site, by jurisdiction and by what is collected. Worth a look before you automate anything at volume.

The Short Answer

Look for the JSON endpoint. In most cases it exists, it is straightforward to call from Apps Script, and it will keep working long after any HTML-based approach would have broken. Reach for a headless browser only once you have confirmed there is nothing behind the page to call.

If you have a source that resists all of these, book a free call. Related: scraping a website into Google Sheets, how to scrape without getting blocked, and why scrapers break.

#scrape dynamic page google sheets#IMPORTHTML#javascript rendering#Google Sheets#סקרייפינג לגוגל שיטס#apps script

Frequently asked questions

Why does IMPORTHTML return nothing for my page?

Because Google Sheets fetches the raw HTML the server returns and never executes JavaScript. Many modern pages return a near-empty shell and populate it in the browser, so Sheets sees the shell rather than the content you can see on screen. Confirm this in thirty seconds: open view-source on the page and search for a value you can see. If it is absent from the source, no Sheets formula will ever retrieve it.

Can Google Apps Script render JavaScript?

No. Apps Script's URL fetch is more capable than the import formulas - it can set headers, handle authentication, parse JSON and run on a schedule - but it performs a fetch, not a browser render. It solves authentication and scheduling problems, not the rendering problem. Where Apps Script becomes genuinely powerful is when you have located the JSON endpoint behind the page, because then no rendering is needed at all.

How do I find the JSON endpoint behind a page?

Open the browser developer tools, switch to the Network tab, filter to Fetch/XHR, and reload the page. Scan the responses for the one containing the values you want. When you find it you have a URL returning clean structured data, which is faster, cheaper and dramatically more stable than parsing rendered HTML - a redesign that changes every CSS class often leaves the data endpoint completely untouched.

When is a headless browser actually necessary?

Only when there is genuinely no accessible endpoint behind the page - for example when the data arrives through a mechanism you cannot replicate, or when it is assembled client-side from several sources. It is the last resort because it is slow relative to a fetch, consumes real memory, and is the most fragile component of any collection system: it breaks on layout changes, on new consent dialogs, and when the site adds bot protection. Treat it as a project with ongoing maintenance, not as a formula replacement.

How often should a scheduled Sheet refresh run?

Match it to how often the source data actually changes, not to how often you look at the sheet. If the underlying data updates daily, a daily trigger writing to a hidden sheet is correct, with your visible formulas reading from there rather than refetching. Running a trigger every few minutes against someone else's site is both discourteous and an efficient way to get blocked. Always write a timestamp alongside the data so nobody mistakes stale values for current ones.

Keep reading

Related service

Web Scraping

Reliable web scraping and data pipelines that deliver clean data.

Learn more

About the author

Yehonatan Saadia

Freelance automation, web & MVP engineer

I'm Yehonatan Saadia, a senior engineer who builds business automation, custom websites, and MVPs for small and mid-sized companies across the US, Europe, and Israel. These guides come from real client work, not theory.

Work with me

Have a project like this?

Tell me what you're trying to automate or build and I'll tell you the fastest reliable way to ship it.