The google sheets importxml imported content is empty error means your formula is trying to scrape a website that relies on client-side JavaScript to render its data. Google Sheets’ native import functions only read static HTML and cannot execute JavaScript, making dynamically loaded tables and prices invisible to your XPath query.
To fix this, inspect the website’s network traffic to find its raw data source, and use a Google Apps Script to fetch the JSON payload directly:
JavaScript
function FETCH_DYNAMIC_JSON(apiUrl, jsonPath) {
const response = UrlFetchApp.fetch(apiUrl);
const data = JSON.parse(response.getContentText());
return data[jsonPath]; // Modify based on exact JSON structure
}
This bypasses the fragile DOM completely. By hitting the hidden API endpoint, you extract the raw data before the target server attempts to format it with unreadable JavaScript.
Prerequisites
- Google Workspace Environment: You must have access to Extensions > Apps Script to deploy custom JavaScript functions within your spreadsheet.
- Chrome DevTools Knowledge: You need to know how to open the Network tab (F12) to intercept the
Fetch/XHRrequests the target website makes to populate its data. - Unblocked Endpoints: The target JSON endpoint must not be strictly protected by anti-bot firewalls like Cloudflare or Datadome, which automatically block Google IPs.
The Syntax Breakdown
This workaround shifts the parsing engine from Google’s default HTML scraper to your own controlled JavaScript execution.
function FETCH_DYNAMIC_JSON(...)— Registers a custom function you can type directly into a spreadsheet cell as=FETCH_DYNAMIC_JSON("url", "path").UrlFetchApp.fetch(apiUrl)— The Apps Script equivalent of a cURL request. It executes a server-side HTTP GET request from Google’s architecture to the hidden data API you discovered via DevTools.JSON.parse(...)— Converts the raw text response from the API into a navigable JavaScript object. This eliminates the need for breakable XPath HTML targeting entirely.data[jsonPath]— Extracts the exact data node you specify. In production environments, you will customize this return statement to map specific array structures from the target API directly onto your 2D sheet grid.
Real-World Example: Competitor Pricing Automation
A retail operations manager maintains a pricing matrix that scrapes a competitor’s storefront using =IMPORTXML("https://store.com/item123", "//span[@class='price']").
Overnight, the competitor migrates their website to a React-based frontend. The pricing elements now load after the initial page request. By morning, the manager’s dashboard is flooded with empty results, wiping out 500 rows of automated pricing intelligence.
The manager opens Chrome DevTools, navigates to the competitor’s product page, filters the Network tab for Fetch/XHR, and discovers the frontend is pulling prices from https://api.store.com/v1/prices/item123.
Raw Data Table (Broken State)
| Product ID | Target URL | IMPORTXML Output |
| SKU-A1 | store.com/item123 | #N/A |
The Formula / Script Apply
The manager deploys a custom Apps Script to hit the JSON API directly and target the currentPrice node.
JavaScript
function GET_PRICE(sku) {
const url = `https://api.store.com/v1/prices/${sku}`;
const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
if (response.getResponseCode() !== 200) return "API Error";
const json = JSON.parse(response.getContentText());
return json.data.currentPrice;
}
She applies the new formula in her sheet as =GET_PRICE(A2).
Output Table (Fixed State)
| Product ID | Target URL | GET_PRICE Output |
| SKU-A1 | api.store.com/v1/prices/SKU-A1 | $45.99 |
Common Related Errors & Fixes
- Exception: Request failed for URL returned code 403 -> Cause: The target API employs bot-protection or expects specific headers that Google’s servers lack. -> Fix: Pass a headers object inside the
UrlFetchAppparameters to spoof a standard browserUser-Agent. If Cloudflare blocks the request outright, you must route the fetch through a residential proxy service. - #ERROR! or Loading… stuck indefinitely -> Cause: You wrapped your custom Apps Script function inside thousands of cells. Google strictly limits concurrent custom function executions, causing severe throttling. -> Fix: Rewrite the Apps Script to accept a range of inputs (array processing) rather than evaluating one cell at a time. This allows you to hit the API in sequential batches.
Key Takeaways
IMPORTXMLonly reads static HTML. If a website renders data dynamically using JavaScript, the function will always return an empty error.- The most robust architectural fix is interrogating the target site’s network traffic to locate the underlying JSON data source.
- Replacing fragile XPath queries with
UrlFetchAppparsing guarantees higher uptime and eliminates DOM-change breakages.
Frequently Asked Questions
Can I force Google Sheets to wait for JavaScript to load?
Google Sheets offers no native mechanism or delay parameter to execute client-side JavaScript. The scraping engine evaluates the raw HTML payload exactly as the target server delivers it upon the initial connection. To render JavaScript, you must rely on third-party middleware APIs like Puppeteer or Selenium to process the DOM before sending it to your sheet.
Why does the XPath work in Chrome DevTools but fail in Google Sheets?
Chrome DevTools evaluates the live DOM after all scripts have executed and rendered the visual elements on your screen. Google Sheets evaluates the source code sent before those scripts ever run. What you see in your active browser tab is a fundamentally different document than what Google’s bot receives.
Does IMPORTHTML suffer from this same JavaScript limitation?
Yes. The IMPORTHTML engine utilizes the exact same static fetching protocol. If a table or list element is injected into the page dynamically after the initial load, it remains completely invisible to the native spreadsheet scraper.