Get Data From a Website Into Google Sheets: Which Method Fits
On this page, 9 sections
To get data from a website into Google Sheets, choose the function by page type. IMPORTHTML reads an HTML table or list. IMPORTXML reads single elements with an XPath query. IMPORTDATA reads a CSV or TSV file, and IMPORTFEED reads RSS or Atom. For JSON, or anything that needs a header or a schedule, use Apps Script.
JSON is the gap. I read the help page for each of the four import functions on 11 October 2026, and none of them lists JSON as a format. That is why API data needs a script, and why most people hit a wall with a formula that was never built for it.
The sitemap post on this site covers one use of IMPORTXML. This one is the map around it: which tool for which page, and the limits that decide when to leave formulas behind.
Pick the function by page type
| Page type | Use | Syntax from Google’s help page | Watch for |
|---|---|---|---|
| An HTML table or list | IMPORTHTML | IMPORTHTML(url, query, index) with query “table” or “list” | Table and list indices are counted separately, both start at 1 |
| One value, or a set of values, inside a page | IMPORTXML | IMPORTXML(url, xpath_query, locale) | You must write the XPath yourself |
| A CSV or TSV file at a URL | IMPORTDATA | IMPORTDATA(url) | Only .csv and .tsv are listed |
| An RSS or Atom feed | IMPORTFEED | IMPORTFEED(url, [query], [headers], [num_items]) | Query defaults to “items” |
| A JSON API | Apps Script with UrlFetchApp | fetch(url) or fetch(url, params) | Runs against quotas, below |
| Another Google Sheet | IMPORTRANGE | Imports a range of cells from a specified spreadsheet | Not for websites |
Google’s own samples are a good starting point: IMPORTHTML("https://en.wikipedia.org/wiki/Demographics_of_India","table",4) for the fourth table on a page, and IMPORTXML("https://en.wikipedia.org/wiki/Moon_landing","//a/@href") for every link.
Check what the page actually serves first
All of these fetch a URL and read what comes back. The help pages do not say whether the page’s JavaScript is run before the function reads it, so I treat that as not documented and test instead. Search the raw HTML for text you can see in the browser:
curl -s "https://example.com/page" | grep -c "text you see in the browser"
A count of 0 means the text is not in the HTML the server sends. A formula is then a gamble. Look for the JSON the page loads in your browser’s network tab, or an official API, and use the script route below.
Test your XPath before it goes in a cell
A wrong XPath gives an empty result and no explanation, so I test them outside the Sheet. I ran three against real files with xmllint, a command-line XPath engine. It is a stand-in for the Sheets engine, not the same code, so treat it as a sanity check.
//*[local-name()='sitemap']/*[local-name()='loc']on my sitemap index returned 4 child sitemaps.//*[local-name()='url']/*[local-name()='loc']on one post sitemap returned 200 URLs.//a/@hrefon my homepage HTML returned 70 links, and//h2matched 8 headings.
The local-name() form matters on sitemaps because they declare an XML namespace. The URL-finding post explains where to get the sitemap in the first place.
When a formula is not enough: Apps Script
Apps Script’s UrlFetchApp.fetch(url) makes the request and returns an HTTP response object you can parse yourself. That is the route for JSON, for custom headers, and for writing results on a schedule. This importer reads a JSON array and writes two columns:
function importJson() {
const url = "https://example.com/api/items.json";
const res = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
if (res.getResponseCode() !== 200) {
throw new Error("HTTP " + res.getResponseCode());
}
const items = JSON.parse(res.getContentText());
const rows = items.map(i => [i.slug, i.link]);
const sheet = SpreadsheetApp.getActiveSheet();
sheet.clearContents();
sheet.getRange(1, 1, 1, 2).setValues([["slug", "link"]]);
sheet.getRange(2, 1, rows.length, 2).setValues(rows);
}
I syntax-checked it with node --check. I then ran it in Node with a stand-in for UrlFetchApp and SpreadsheetApp, feeding it 20 real records from my own site’s REST API. It wrote a header row and 20 rows of two columns. I have not run it inside Apps Script, so test it on a copy of your sheet first.
Replace slug and link with the keys in your JSON. If the response is an object rather than an array, items.map will throw, so open the URL in a browser and look at the shape first.
The limits that decide which route you use
These are from the Apps Script quotas page and the Drive file limits page, both read on 11 October 2026:
| Limit | Consumer account | Google Workspace |
|---|---|---|
| URL Fetch calls per day | 20,000 | 100,000 |
| Script runtime | 6 min per execution | 6 min per execution |
| Custom function runtime | 30 sec per execution | 30 sec per execution |
| Cells in a spreadsheet | Up to 20 million cells or 100MB | Up to 20 million cells or 100MB |
The 6 minutes against 30 seconds is the practical rule. A custom function that fetches a page has 30 seconds. A script run from a menu or a trigger has 6 minutes, which is 360 seconds, twelve times as long. The cell limit is a Drive file limit for sheets created in or converted to Google Sheets.
What to use for common jobs
- A price table on a page:
IMPORTHTML, then check the index. - A list of links from a page or sitemap:
IMPORTXMLwith//a/@hrefor the sitemap XPath above. - A published CSV:
IMPORTDATA. - A blog’s latest posts:
IMPORTFEED. - A JSON API such as YouTube’s: a script. The YouTube import post does this with the YouTube API.
If you want the opposite direction, a sheet that feeds a web page, see turning a Google Sheet into a website.
One rule for all of it: only pull from sites you own or have permission to read, and check robots.txt and the site’s terms before you schedule anything.
What I could not verify
- JavaScript rendering: the four help pages do not say whether scripts on the page are run. I treat it as not documented.
- Refresh timing: I did not find a refresh interval on the pages I read, so I do not give one.
- Per-site rate limits: a site can block repeated requests. That is the site’s rule, not Google’s, and I did not test any third-party site for this post.
- Behaviour inside Sheets: I could not open a spreadsheet from this session, so every formula in this post is Google’s own sample or was tested outside Sheets.
That list is also a to-do. Each item can be settled in ten minutes with a throwaway sheet before you build anything on top of it.
Questions people ask
Can Google Sheets import JSON?
Not through the four import functions: their help pages list HTML, XML, CSV, TSV, RSS and Atom, and none mention JSON. Use Apps Script.
Why does IMPORTXML return nothing?
The XPath matched nothing in the HTML the function received. Check the raw source with the curl line above and test the XPath in a command-line tool first. I did not find an error list in Google’s help page.
How many fetches can a script make per day?
20,000 URL Fetch calls on a consumer account and 100,000 on Google Workspace, per the quotas page read on 11 October 2026.
Do this today
Pick one page you copy data from by hand. Run the curl check on it. If the text is in the HTML, use the table above. If it is not, find the JSON behind it and adapt the script.
Resources
- IMPORTXML, Google Docs Editors Help, read 11 October 2026
- IMPORTHTML, Google Docs Editors Help, read 11 October 2026
- IMPORTDATA, Google Docs Editors Help, read 11 October 2026
- IMPORTFEED, Google Docs Editors Help, read 11 October 2026
- UrlFetchApp, Apps Script reference, read 11 October 2026
- Apps Script quotas, read 11 October 2026
- Files you can store in Google Drive, read 11 October 2026