Aditya Sharma

IMPORTXML Not Working in Google Sheets: Find the Cause in 5 Steps

On this page, 9 sections

IMPORTXML stops working when the thing it fetches is not the thing you see in your browser. The usual suspects are an XPath that does not match the HTML the server sends, content that is not in that HTML at all, a server that refuses the request, or a URL typed without its protocol.

Google’s help page for IMPORTXML documents the syntax and nothing about errors, so you have to find out which one you have. This post is the order I would test them in, cheapest first, and the commands to reproduce the failure outside Sheets.

I did not run IMPORTXML in a live sheet for this post. What I ran is the fetch-and-XPath check from a terminal on 11 October 2026, and the output of each command is shown below.

What Google documents, and what it does not

I read the IMPORTXML help page on 11 October 2026. It gives the syntax IMPORTXML(url, xpath_query, locale), says the URL must include the protocol, and says the URL must be in quotation marks or come from a cell reference. It describes the function as importing from XML, HTML, CSV, TSV, and RSS and ATOM feeds.

It lists no error messages, no limits, and no statement on whether the page’s JavaScript is run. Google’s separate page on user-triggered fetchers, which I also read that day, does not mention Sheets anywhere in its text, so the user agent Sheets sends is not documented there.

The one relevant thing Google does say, on its page about optimising data references, is that pulling data from another spreadsheet with IMPORTRANGE needs a round trip and can bring delays and intermittent connections. That page does not say the same about IMPORTXML, so a formula that fails once is worth retrying after a refresh before you change the XPath.

Step 1: Prove the fetch works with a query that cannot fail

Put this in a cell, with your own URL and the protocol included:

=IMPORTXML("https://example.com/page", "//title")

If you get the page title back, the fetch works and your problem is the XPath or the content. If you get an error, your problem is the fetch, so skip to step 4. This one cell splits the problem in half.

Step 2: Look at what the server sends, not what the browser shows

A browser shows the page after its JavaScript has run. A plain request gets the response body only. Fetch it yourself and run the same XPath. This uses curl and xmllint, which ships with macOS and most Linux distributions:

curl -s -A "Mozilla/5.0 (xpath-check)" -o page.html https://example.com/page
xmllint --html --xpath '//title/text()' page.html

I ran this against my own homepage on 11 October 2026. The server answered 200 with 112,270 bytes, the title query returned Aditya Sharma, Marketer Who Codes, and count(//h2) returned 8. That page is server-rendered, so what a plain request gets matches what a browser shows.

Now the failing case. I wrote a six-element page of the kind a JavaScript app sends: an empty div with an id of root and a script tag. Running xmllint --html --xpath '//h2' against it printed XPath set is empty. Nothing in the response body is an h2, because the headings only exist after the script runs.

If your XPath is empty here but matches in the browser’s inspector, you have found the cause. I cannot tell you from the docs whether Sheets runs scripts, since the help page is silent on it. The check above tests the part you can control: if the data is in the response body, an XPath can reach it.

Step 3: Fix the XPath against the raw HTML

Write your query against the response body, not against the inspector’s tree. A common mismatch is the table body.

Browsers insert a tbody element when parsing a table, so an inspector copy of the path often contains /tbody/, while the raw HTML may not have one. I wrote a two-row table with no tbody and ran two queries on it with libxml2’s HTML parser:

  • count(//table/tbody/tr) returned 0.
  • count(//table//tr) returned 2.

The docs do not say which parser Sheets uses, so test both forms in your formula and keep the one that returns data. The version with // between table and row survives either way.

Other points to check:

  • Use //a/@href for link addresses, as Google’s own example does, and //h2 for text.
  • Quotation marks inside the query clash with the quotes around it. Use single quotes inside a double-quoted query, or put the query in a cell and reference it.
  • A row that comes back with extra empty cells is usually a query that matches a wrapper element as well as the item you wanted. Tighten it with an attribute test such as //div[@class='price'].

Step 4: When the server will not answer

If the //title test in step 1 fails, run the same URL through curl with -I to see the status code. A 403 or 429 means the site is refusing automated requests, and the answer is to stop, not to retry in a loop. Hammering a site that has told you no is how you get an address blocked.

Check whether the site publishes a feed, a sitemap or an API for the same data. Those are meant to be fetched, and they are usually smaller than the page. The related functions Google lists next to IMPORTXML cover some of them: IMPORTFEED for RSS and ATOM, and IMPORTDATA for CSV and TSV.

Step 5: When the content is not in the response body

Open the browser’s network tab, reload, and look for a request that returns JSON. Many pages load their data from one. If you find it, you can often fetch that address directly, and a JSON endpoint is easier to parse than HTML.

If there is no such request, move the fetch into Apps Script. UrlFetchApp issues HTTP and HTTPS requests from a script, and its reference page (last updated 15 May 2026) documents muteHttpExceptions so you can read a failing status code instead of getting an exception.

Google’s quotas page, last updated 3 September 2026, lists 20,000 URL Fetch calls a day for consumer accounts and 100,000 for Google Workspace accounts, and 6 minutes per script execution. The head-to-head with the import functions is in IMPORTHTML vs IMPORTXML vs Apps Script.

For the simpler case of pulling a list of pages, a sitemap is the cleaner source, and I wrote that one up in extracting links from a sitemap in Sheets. The broader starting point is getting data from a website into Google Sheets.

What I could not verify

I could not verify the exact error text Sheets shows for each failure, because Google does not document it and I did not run the function in a live sheet. I also could not verify which user agent or HTML parser Sheets uses. The steps above are built to get you an answer without needing either.

Do this today

Take the formula that fails and replace its XPath with //title. If that returns a title, run the curl and xmllint pair from step 2 with your original XPath and see whether the response body contains your data. That one comparison tells you whether to fix the query or change the source.

Resources

Tell me where I am wrong

Your email is not published and I do not add it to any list. Corrections with a source are the ones I act on fastest.