Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
Skip to content
Apps Script

How to Send Web Scraping Results to Google Sheets (API and Apps Script)

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To send web-scraped data to Google Sheets, build a four-stage pipeline: fetch pages you are permitted to access, extract and normalize each record into a fixed set of columns, authorize the destination spreadsheet, and append or update rows. For an external scraper, the Google Sheets API and spreadsheets.values.append are the most direct route. If the workflow belongs inside Google Workspace, Apps Script can fetch pages with UrlFetchApp and write directly to a sheet.

This guide shows both approaches, including authentication, row preparation, append versus update behavior, quotas, retries, and failure handling. Permission to collect data depends on the source site, its terms, your access method, and applicable law; Google Sheets documentation does not decide whether a particular scrape is allowed.

Choose the architecture before writing code

Option Where it runs Best fit Important constraints
Python plus Sheets API Your computer, server, container, or scheduled job Separate scraping and storage, custom libraries, larger workflows You manage deployment, credentials, scheduling, retries, and network access
Google Apps Script Inside a Google Workspace project Small or moderate jobs that should live with the spreadsheet Apps Script execution and URL Fetch quotas apply; check limits for your account and schedule

Both designs use the same data model: a two-dimensional array in which every inner array is one spreadsheet row. Define stable columns first—for example, url, title, price, scraped_at—then convert missing values to an intentional empty string or a documented placeholder. Validate dates, numbers, URLs, and text before sending a batch.

Prepare Google Sheets and credentials

  1. Create or select a Google Cloud project. Enable the Google Sheets API in that project.
  2. Identify the spreadsheet. Copy the ID between /d/ and /edit in its URL. Create a worksheet and put your header row in row 1.
  3. Select an authorization model. Google’s Python quickstart demonstrates OAuth user authorization and describes that simplified flow as suitable for testing. Production credentials should match your application’s actual access pattern; do not put secrets in source code or commit token files.
  4. Share access where required. The identity used by your application must have permission to edit the spreadsheet. Use the narrowest OAuth scope that meets the job. The append method requires an authorized Sheets scope; Google also documents Drive scopes for some access patterns.

Google describes the values resource as the API for reading and writing cell values. The official Python quickstart covers enabling the API and creating credentials: Python quickstart.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Python: scrape, normalize, and append rows

The example below uses a permitted public HTML page, Beautiful Soup for extraction, and the Google client library for writing. Replace the selector and URL with a source you are allowed to collect. The scraper deliberately separates extraction from the Sheets write so malformed records can be rejected before they reach the spreadsheet.

Install dependencies

python -m pip install requests beautifulsoup4 google-api-python-client google-auth-httplib2 google-auth-oauthlib

Complete example

from datetime import datetime, timezone
from pathlib import Path
import requests
from bs4 import BeautifulSoup
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build

SCOPES = ["https://www.googleapis.com/auth/spreadsheets"]
SPREADSHEET_ID = "YOUR_SPREADSHEET_ID"
RANGE_NAME = "Data!A:D"
SOURCE_URL = "https://example.com/items"


def fetch_rows(url):
    response = requests.get(
        url,
        timeout=30,
        headers={"User-Agent": "authorized-data-collector/1.0"},
    )
    response.raise_for_status()
    soup = BeautifulSoup(response.text, "html.parser")
    scraped_at = datetime.now(timezone.utc).isoformat()
    rows = []
    for card in soup.select("article.item"):
        title_node = card.select_one(".title")
        price_node = card.select_one(".price")
        link_node = card.select_one("a")
        title = title_node.get_text(" ", strip=True) if title_node else ""
        price = price_node.get_text(" ", strip=True) if price_node else ""
        link = link_node.get("href", "") if link_node else ""
        if title and link:
            rows.append([link, title, price, scraped_at])
    return rows


def get_credentials():
    creds = None
    token = Path("token.json")
    if token.exists():
        creds = Credentials.from_authorized_user_file(token, SCOPES)
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                "credentials.json", SCOPES
            )
            creds = flow.run_local_server(port=0)
        token.write_text(creds.to_json())
    return creds


def append_rows(rows):
    if not rows:
        print("No valid records; nothing to write.")
        return
    service = build("sheets", "v4", credentials=get_credentials())
    body = {"values": rows}
    result = service.spreadsheets().values().append(
        spreadsheetId=SPREADSHEET_ID,
        range=RANGE_NAME,
        valueInputOption="USER_ENTERED",
        insertDataOption="INSERT_ROWS",
        body=body,
    ).execute()
    print(result.get("updates", {}))


if __name__ == "__main__":
    append_rows(fetch_rows(SOURCE_URL))

Download an OAuth client file as credentials.json from the Cloud project and run the script interactively once. The quickstart flow is a testing convenience, not a universal production credential design. For a server, use an access pattern appropriate to that server, protect tokens with a secret manager, and grant only the required spreadsheet access.

What the append call actually does

Google’s append reference says the method “Appends values to a spreadsheet.” It searches the supplied range for an existing data table and writes at the next row. valueInputOption controls how submitted text is interpreted—RAW keeps strings as supplied, while USER_ENTERED lets Sheets parse dates, numbers, and formulas. It does not choose the starting cell. Use values.update for a fixed range or values.batchUpdate for multiple ranges. See Google’s read and write values guide and the append reference.

Apps Script: keep fetching and writing in Google Workspace

Apps Script is convenient when a spreadsheet owns the workflow. In the script editor, add this function, replace the URL and sheet name, then run it and approve the requested permissions.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
function scrapeAndAppend() {
  const url = 'https://example.com/items';
  const sheet = SpreadsheetApp
    .openById('YOUR_SPREADSHEET_ID')
    .getSheetByName('Data');

  const response = UrlFetchApp.fetch(url, {
    muteHttpExceptions: true,
    headers: { 'User-Agent': 'authorized-data-collector/1.0' }
  });
  if (response.getResponseCode() !== 200) {
    throw new Error('Source returned HTTP ' + response.getResponseCode());
  }

  const html = response.getContentText();
  const rows = extractRows_(html);
  if (rows.length === 0) return;

  sheet.getRange(sheet.getLastRow() + 1, 1, rows.length, rows[0].length)
       .setValues(rows);
}

function extractRows_(html) {
  // Replace this parser with extraction suited to your permitted source.
  // Apps Script has no built-in CSS HTML parser; use an API or carefully
  // scoped text/regex logic for simple, stable markup.
  const timestamp = new Date().toISOString();
  return [[ 'https://example.com/item-1', 'Example item', '', timestamp ]];
}

UrlFetchApp fetches HTTP and HTTPS resources. If your project declares scopes explicitly, include https://www.googleapis.com/auth/script.external_request. Apps Script can write through spreadsheet services as shown, or through the Sheets API advanced service. Add a time-based trigger only after estimating URL volume, execution duration, and quota consumption. Google’s Apps Script quota table lists 20,000 URL Fetch calls per day for consumer accounts and 100,000 per day for Workspace accounts; quotas can change. See Apps Script quotas and UrlFetch authorization details.

Prevent duplicates and bad rows

Use a stable key

Appending blindly creates duplicates whenever a scheduled job sees the same page twice. Choose a key such as canonical URL, product ID, or source ID. Load existing keys into a set, discard or update records that already exist, and append only new rows. If two workers can run concurrently, serialize the job or use a lock; otherwise both may pass the duplicate check.

Normalize before writing

  • Convert relative links to absolute URLs and remove tracking parameters when that is valid for your source.
  • Use one date format, preferably an explicit UTC timestamp such as ISO 8601.
  • Keep numeric fields numeric when you need sorting or formulas; remove currency symbols before conversion.
  • Escape or reject values that begin with =, +, -, or @ if untrusted text could be interpreted as a spreadsheet formula.
  • Log rejected records with the reason instead of silently dropping them.

Quotas, payload size, and reliable retries

Google lists per-minute Sheets API limits of 300 read requests per project and 60 read requests per user per project, plus 300 write requests per project and 60 write requests per user per project. These are documented limits, not a performance guarantee. Batch rows so one request carries many records; Google recommends a maximum payload of about 2 MB for performance even though the API documentation does not define that as a hard request-size limit. Batch calls count as one request, and API writes are applied atomically.

For HTTP 429 or other time-based quota errors, retry with truncated exponential backoff: wait progressively longer (for example, 1, 2, 4, 8 seconds with jitter), cap the delay and number of attempts, and avoid retrying permanent 4xx errors. Persist a checkpoint or source cursor so a retry does not repeat an entire crawl. Apps Script jobs must also respect execution-time and URL Fetch quotas; split large jobs into trigger-driven batches.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Troubleshooting

401 or 403 from Sheets

The token may lack the required scope, be expired, or belong to an identity without edit access. Reauthorize with the Sheets scope, share the spreadsheet with the correct account, and verify the spreadsheet ID.

Rows appear in the wrong place

Append searches the table in the supplied A1 range. Check the worksheet name, header row, and range such as Data!A:D. For a known cell, use update instead of append.

Numbers or dates become text

Choose USER_ENTERED when Sheets should parse values, or RAW when exact strings matter. Normalize the input and inspect the resulting cell format.

429 RESOURCE_EXHAUSTED

Reduce request frequency, increase batch size, and apply truncated exponential backoff. Track both project and per-user limits.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Apps Script says authorization is required

Run the function manually once, review the requested scopes, and ensure an explicit manifest includes script.external_request when you use UrlFetchApp.

The source returns a CAPTCHA, empty HTML, or JavaScript-only content

Do not attempt to bypass access controls. Use a documented API or an allowed export where available. If you are authorized to capture a rendered page, a browser-based screenshot or rendering service may be more appropriate than raw HTTP parsing.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your workflow needs rendered pages rather than raw HTML, ScreenshotNeo provides a website screenshot API and MCP server. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses identify the result with X-Page-Verdict and X-Billed headers.

One GET request returns PNG, JPEG, WebP, or a PDF. The API supports full-page lazy-image loading, CSS-selector element capture, device and viewport settings, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage data, and an OpenAPI specification. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

See the ScreenshotNeo documentation for options and response headers. An MCP server lets AI agents take screenshots. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

FAQ

Can I write directly to a fixed cell instead of appending?

Yes. Use the Sheets values update operation for a known A1 range, or batchUpdate for multiple ranges. Append is intended for adding records to the next row of a detected table.

Should I scrape in Apps Script or Python?

Use Apps Script when the spreadsheet and schedule are central to a Google Workspace workflow. Use Python when you need an independent runtime, richer parsing libraries, or infrastructure you already operate.

Does Google authorize scraping a website?

No. Google’s API documentation covers spreadsheet access, not permission to collect a particular site. Check the source’s terms, robots guidance, access controls, and applicable requirements.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.