How to Scrape Website Data Into Google Sheets
To scrape a website into Google Sheets, type a built-in import formula into a cell: IMPORTHTML pulls a table or list, IMPORTXML extracts any element by its XPath, and IMPORTDATA loads a CSV file. For anything those formulas cannot reach, an Apps Script fetches the page on a timer and writes the rows. All of these run on Google's servers and read only the HTML the site sends back, so they cannot render JavaScript or use a proxy. When a site blocks them, the next step is a small script that runs through ProxyField residential proxies from $1.95/GB and writes into the same sheet; start with the Easy Trial: 1 GB for $2.95.
- residential IPs
- 412M+
- from, traffic never expires
- $1.95/GB
- Easy Trial
- 1 GB for $2.95
On this page 10 sections
- Which Google Sheets method to use
- Scrape a table with IMPORTHTML
- Extract any element with IMPORTXML and XPath
- Scrape many URLs into one sheet
- How often the import formulas refresh
- A price tracker with Apps Script
- Can Google Sheets scrape JavaScript websites?
- Why IMPORTXML fails, and what the errors mean
- When to move from Sheets to a script with proxies
- How to Scrape Website Data Into Google Sheets FAQ
Which Google Sheets method to use
Google Sheets offers four import formulas and one scripting route. Pick by what the data looks like on the page:
| Method | What it imports | Syntax | Good for | Limits |
|---|---|---|---|---|
IMPORTHTML | One HTML table or list | =IMPORTHTML( | Tables of rankings, specs, schedules | Only real <table>, <ul> or <ol> markup |
IMPORTXML | Any element or attribute, by XPath | =IMPORTXML( | Titles, prices, links, meta tags | You write the XPath; one value list per formula |
IMPORTDATA | A CSV or TSV file at a URL | =IMPORTDATA(url) | Open data files and CSV exports | The file must be public |
IMPORTFEED | An RSS or Atom feed | =IMPORTFEED(url) | News and blog updates | Only sites that publish a feed |
Apps Script with UrlFetchApp | Anything you can fetch and parse in JavaScript | A script, run by hand or on a timer | Price history, many URLs, APIs | Daily fetch quotas; no proxy option |
The function syntax is documented in Google's Docs Editors Help (IMPORTHTML, IMPORTXML, IMPORTDATA, IMPORTFEED).
Scrape a table with IMPORTHTML
IMPORTHTML is the fastest way to scrape a table: paste the formula, and the whole table appears in the sheet with its header row.
- Open the page and find the table you want.
- In an empty cell, type the formula with the page URL,
"table", and the table's position on the page (1 for the first table). - If you get the wrong table, change the index to 2, 3 and so on until the right one appears.
For example, this formula imports the 25-row hockey-teams table from scrapethissite.com, a site built for scraping practice:
=IMPORTHTML("https://www.scrapethissite.com/pages/forms/", "table", 1)Use "list" instead of "table" to import a bulleted or numbered list. To keep only some columns, wrap the formula in QUERY or CHOOSECOLS, for example =CHOOSECOLS(IMPORTHTML(…), 1, 2, 3) for the first three. Tables built from <div> elements, which many modern sites use, are not tables to IMPORTHTML; use IMPORTXML for those.
Extract any element with IMPORTXML and XPath
IMPORTXML takes a URL and an XPath query and returns every element that matches, one per row. It is how you scrape prices, titles, links and other values that do not sit in a table.
To find an XPath, open the page in Chrome, right-click the value, choose Inspect, then right-click the highlighted element in DevTools and choose Copy > Copy XPath. Copied paths are long and break easily, so shorten them to the element and its class. These examples run on books.toscrape.com, another practice site:
| What you want | Formula |
|---|---|
| The page heading | =IMPORTXML("https:// |
| All 20 book titles on the page | =IMPORTXML("https:// |
| All 20 prices | =IMPORTXML("https:// |
| Every link URL | =IMPORTXML( |
| The meta description | =IMPORTXML( |
We checked the three books.toscrape.com queries with the same XPath expressions in Python's lxml on 2026-10-23: 20 titles and 20 prices came back. For the XPath syntax itself, see HTML scraping.
Scrape many URLs into one sheet
To scrape a list of pages, put the URLs in column A and one formula per row in column B that refers to the cell, not to a typed URL:
A2: https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html
B2: =IFERROR(INDEX(IMPORTXML(A2, "//p[@class='price_color']"), 1), "check")Drag B2 down the column. INDEX(…, 1) keeps only the first match, so each URL fills one cell, and IFERROR marks failed rows instead of filling the sheet with errors. Keep such lists to dozens or a few hundred rows: each formula is a separate fetch from Google's servers, and large sheets stay on "Loading…" for a long time.
How often the import formulas refresh
Google's help center states that IMPORTHTML, IMPORTXML, IMPORTDATA and IMPORTFEED recalculate about once an hour while the spreadsheet is open. The "Recalculation" setting under File > Settings speeds up only time functions such as NOW(), not imports. Two consequences follow:
- The values change in place. Yesterday's price is overwritten by today's, so a formula alone gives you no history.
- You cannot force a fast refresh from the formula. A common workaround adds a dummy query parameter that a cell changes, which makes Sheets fetch again, but it multiplies your requests to the site.
For a history, copy the values on a schedule with Apps Script, as in the next section.
A price tracker with Apps Script
Apps Script, under Extensions > Apps Script, runs JavaScript on Google's servers with access to your sheet. UrlFetchApp.fetch() downloads a page, and a time-driven trigger runs the script every day, so you keep one row per product per day.
The script below reads product names and URLs from a "Products" sheet (columns A and B), finds the price in each page's structured data and appends a dated row to a "History" sheet:
function trackPrices() {
const ss = SpreadsheetApp.getActive();
const products = ss.getSheetByName('Products');
const history = ss.getSheetByName('History');
const rows = products.getRange(2, 1, products.getLastRow() - 1, 2).getValues();
rows.forEach(([name, url]) => {
const res = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
if (res.getResponseCode() !== 200) {
history.appendRow([new Date(), name, '', 'HTTP ' + res.getResponseCode()]);
return;
}
// Many product pages publish the price in JSON-LD: "price": "19.99"
const match = res.getContentText().match(/"price"\s*:\s*"?([0-9]+(?:\.[0-9]+)?)/);
history.appendRow([new Date(), name, match ? Number(match[1]) : '', match ? 'ok' : 'no price']);
Utilities.sleep(2000); // be polite: one request every 2 seconds
});
}To run it daily, open Triggers (the clock icon) in the Apps Script editor, add a trigger for trackPrices, choose "Time-driven" and a day timer. Apps Script has no HTML parser like a browser's DOM, so scripts read values with regular expressions or from the JSON-LD block that many shops add for search engines. Check the regular expression on your own target pages, because shops format prices differently.
Google lists daily quotas for Apps Script on its quotas page: 20,000 URL fetch calls a day for consumer accounts and 100,000 for Google Workspace accounts, and 6 minutes per script run. Split long URL lists over several runs.
Can Google Sheets scrape JavaScript websites?
No. Google Sheets cannot scrape content that a page builds with JavaScript. The import formulas and UrlFetchApp receive the HTML the server sends, before any script runs, so prices or reviews loaded later come back empty or as "Imported content is empty". Three ways around it:
- Fetch the data the page loads. Open DevTools, go to the Network tab, filter by "Fetch/XHR", and reload. If the data comes from a JSON URL that works without a login,
UrlFetchAppcan fetch it andJSON.parse()turns it into rows. More in how to scrape hidden APIs. - Use a scraping API with rendering, which loads the page in a browser on its own servers. ProxyField's web scraping API fetches any URL with JavaScript rendering for $2.00 per 1,000 successful results, down to $1.00, billed on successful results only, through Contact sales.
- Scrape outside Sheets with a headless browser such as Playwright, and write the results into the sheet, as below. See scraping dynamic web pages.
Why IMPORTXML fails, and what the errors mean
| Message | Usual cause | What to try |
|---|---|---|
| "Could not fetch url" | The site refused Google's request, timed out, or blocks bots | Check that the page loads logged out; if it does, the site is blocking Google's servers |
| "Imported content is empty" | The XPath matched nothing, or the data is loaded by JavaScript | Test the XPath on the page source (Ctrl+U), not on the DevTools view |
| "Resource at url contents exceeded maximum size" | The page is too large | Import a smaller page, or use Apps Script |
| Stuck on "Loading…" | Too many import formulas at once | Fewer formulas per sheet; move bulk work to a script |
Many sites block the import formulas, because every request comes from Google's server IPs, and the user agent names Google's apps rather than a browser. A shop that limits bots sees thousands of spreadsheets sharing the same small set of addresses. Neither the formulas nor UrlFetchApp lets you set a proxy, so you cannot change those IPs from inside Sheets.
When to move from Sheets to a script with proxies
Move the scraping out of Sheets, and keep Sheets as the place the data lands, when any of these is true:
- the site answers Google's servers with errors or empty pages;
- you need more than a few hundred URLs, or more often than hourly;
- the data needs JavaScript rendering or a session you are entitled to use;
- you need pages as seen from a given country or city.
The pattern: a Python script fetches the pages through a proxy and appends the rows with the open-source gspread library, using a Google service account that has edit access to the sheet.
# pip install requests beautifulsoup4 gspread
import datetime, time
import gspread, requests
from bs4 import BeautifulSoup
PROXY = "http://<login>-country-us-city-new_york:<password>@gate.proxyfield.io:7000"
urls = ["https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html"]
rows = []
for url in urls:
html = requests.get(url, proxies={"http": PROXY, "https": PROXY}, timeout=30).content
soup = BeautifulSoup(html, "html.parser")
rows.append([datetime.date.today().isoformat(), soup.h1.get_text(strip=True),
soup.select_one("p.price_color").get_text(strip=True), url])
time.sleep(2)
sheet = gspread.service_account(filename="service-account.json").open("Price tracker").worksheet("History")
sheet.append_rows(rows, value_input_option="USER_ENTERED")<login>-country-us-city-new_york asks the ProxyField gateway for an exit IP in New York. Region, session and session time go in the same username: -region-<region>, -session-<id> and -ttl-<seconds> (30 to 129,600 seconds, 20 minutes by default), as in <login>-country-us-region-new_york-city-new_york-session-he9f2qga-ttl-600; every part after the login is optional. Run the script on a schedule with cron or a cloud scheduler: see how to schedule web scrapers. Full proxy setup for Python: Python proxy setup.
ProxyField residential proxies rotate the IP on every request, or keep one IP for 30 s to 36 h, with targeting by country, region (state) and city. You pay per GB with no subscription, and the traffic never expires; all purchases are final. At an assumed 150 KB per product page, 1 GB covers roughly 6,600 pages. If you work in Excel rather than Sheets, see how to scrape data from a website into Excel. Scrape public data only: is web scraping legal.
How to Scrape Website Data Into Google Sheets FAQ
How do I scrape data from a website into Google Sheets?
ProxyField residential proxies start at $1.95/GB; to scrape data from a website into Google Sheets, type =IMPORTHTML(url, "table", 1) for a table, or =IMPORTXML(url, xpath) for any other element, and the data appears in the sheet. For a daily history or many URLs, use an Apps Script on a timer. For sites that block Google's servers, run a script through those residential proxies that writes into the same sheet.
Can I store my scraped data in Google Drive or Google Sheets?
Yes. A Python scraper can append rows to a sheet with the gspread library and a Google service account, or save CSV files to Google Drive through the Drive API. Most no-code scrapers also export straight to Google Sheets. Sheets suits thousands of rows; for millions, use a database and connect Sheets to a summary.
How do I get web data into Google Sheets automatically?
The import formulas refresh on their own about once an hour while the file is open, but they overwrite the old values. To keep a history, write an Apps Script that fetches the pages and appends dated rows, and give it a time-driven trigger. For sites that block Google's servers, schedule an outside script that writes to the sheet instead.
What is the difference between IMPORTXML and IMPORTHTML?
IMPORTHTML imports a whole HTML table or list, picked by its position on the page. IMPORTXML returns whatever an XPath query matches, such as every price, title, link or meta tag, so it works on pages without real tables. Use IMPORTHTML when the data is already a table, and IMPORTXML for everything else.
Can Google Sheets scrape JavaScript-rendered pages?
No. The import formulas and Apps Script read the HTML the server sends before any script runs, so content loaded later comes back empty. Fetch the JSON request the page makes instead, call a scraping API with rendering from Apps Script, or scrape with a headless browser outside Sheets and write the rows in.
Is Google Sheets good for web scraping?
For small jobs, yes: a few tables or a few hundred values from public pages that serve plain HTML, refreshed hourly. It falls short on JavaScript pages, on sites that block Google's servers, on logins, and on anything above a few hundred URLs. Past that, a script or a scraping tool that writes into Sheets works better.
Can I use a proxy with Google Sheets web scraping?
Not from inside Sheets: neither the import formulas nor Apps Script's UrlFetchApp lets you set a proxy, so requests always come from Google's servers. To fetch through a proxy, run the scraper outside Sheets, for example in Python with a proxy URL, and push the results into the sheet with gspread.
Can Apps Script scrape a website?
Yes, for pages that serve their data in the HTML. UrlFetchApp.fetch(url) downloads the page, and you read values with regular expressions or from the page's JSON-LD block, because Apps Script has no browser DOM. Google sets daily quotas on fetches: 20,000 a day for consumer accounts and 100,000 for Workspace accounts.
How do I scrape multiple URLs into one Google Sheet?
List the URLs in column A and put one formula per row that refers to the cell, such as =IFERROR(INDEX(IMPORTXML(A2, "//h1"), 1), "check"), then drag it down. Keep it to dozens or a few hundred rows, because each formula is a separate fetch. For longer lists, loop over the URLs in Apps Script or an outside script.
How often does IMPORTXML refresh?
About once an hour while the spreadsheet is open, according to Google's help center; the same applies to IMPORTHTML, IMPORTDATA and IMPORTFEED. The recalculation setting in File > Settings does not make imports refresh faster. If you need fixed snapshots at set times, copy the values with a timed Apps Script.
Show 1 more questionShow fewer questions
Why does IMPORTXML say "Could not fetch url"?
Usually because the site refused or timed out on Google's request. Many sites block the shared server IPs that all spreadsheets fetch from, and some pages need a login or a cookie. Check that the page loads logged out in a private window; if it does, the block is on Google's servers, and only a scraper outside Sheets can reach it.
Related pages
Easy Trial: 1 GB for $2.95.
Try for the price of 1 GB, no subscription, traffic never expires.