How to Capture HTML or Website Screenshots in Google Sheets
Google Sheets can display images, but it cannot render a webpage URL as a screenshot by itself. Use Apps Script with a browser screenshot API to automate captures.
Direct answer: Google Sheets does not turn a webpage URL into a screenshot by itself. IMAGE() displays an image from an image URL, while IMPORTHTML() extracts a table or list. To capture a rendered website automatically, use Google Apps Script to call a browser-based screenshot API, then insert the returned image into the sheet. For a one-off capture, take a screenshot first and insert it through Sheets.
Choose the right method
| Method | When to use it | What ends up in the sheet |
|---|---|---|
| Insert an image manually | A one-off screenshot or a small number of pages | An image placed in a cell or over cells |
IMAGE() |
You already have a direct image URL | An image displayed from that URL |
| Apps Script plus a browser screenshot API | You need repeatable captures of webpage URLs | Image bytes inserted into the sheet, or a returned image URL displayed by a formula |
Google documents the image and HTTP building blocks. The API workflow below is an implementation pattern: the browser renderer performs the visual capture, while Apps Script makes the request and writes the result into Sheets. Google Sheets: insert or delete images, IMAGE function, IMPORTHTML function, and Apps Script UrlFetchApp.
Insert a screenshot manually
- Capture the webpage with your browser or operating system screenshot tool.
- In Google Sheets, choose Insert → Image, then choose an image-in-cell or image-over-cells placement.
- Pick or upload the captured file and adjust the row or column dimensions to suit it.
An image in a cell occupies that cell, so it cannot also hold text. An image over cells can sit above cells that still contain text. Sheets also documents taking a screenshot from the image insertion flow. This is usually the least setup for a single page.
Display an existing image URL with IMAGE()
When the URL already points directly to an image that Sheets can load, put it in a cell such as A2, then use:
=IMAGE(A2)
Or specify a display mode:
=IMAGE(A2, 1)
=IMAGE(A2, 2)
=IMAGE(A2, 3)
=IMAGE(A2, 4, 240, 400)
| Mode | Behavior |
|---|---|
| 1 (default) | Fits the image inside the cell while preserving its aspect ratio. |
| 2 | Stretches the image to fill the cell, which can distort it. |
| 3 | Displays the image at its original size. |
| 4 | Uses the specified custom height and width in pixels. |
IMAGE() does not resize the cell. Adjust row height and column width if the image needs more space. Google documents that Drive-hosted URLs and SVG files are unsupported by this function, and that its base URL cannot be a volatile function, directly or indirectly. If a screenshot API returns image bytes rather than a stable, accessible image URL, this formula is not the right way to place those bytes; use Apps Script to insert a blob or use a provider’s supported image URL flow.
Automate webpage screenshots with Apps Script
This example calls ScreenshotNeo’s screenshot endpoint and inserts the returned image blob into the active sheet. It captures each URL in column A, starting at row 2, and places the image on the same row. It uses the endpoint’s default image format; the API returns an image or PDF depending on the request options. See the ScreenshotNeo API documentation for the available parameters and formats.
1. Add the script
Open Extensions → Apps Script in the spreadsheet and add this code. Replace the placeholder key with your API key. For a shared or production script, keep the secret out of visible cells and follow the provider’s credential guidance.
const SCREENSHOT_API = 'https://api.screenshotneo.com/v1/shot';
const API_KEY = 'YOUR_API_KEY';
function captureWebsitesInSheet() {
const sheet = SpreadsheetApp.getActiveSheet();
const firstRow = 2;
const urlColumn = 1;
const imageColumn = 2;
const lastRow = sheet.getLastRow();
for (let row = firstRow; row <= lastRow; row++) {
const pageUrl = String(sheet.getRange(row, urlColumn).getValue()).trim();
if (!pageUrl) continue;
const response = UrlFetchApp.fetch(SCREENSHOT_API, {
method: 'get',
payload: { access_key: API_KEY, url: pageUrl },
muteHttpExceptions: true
});
const status = response.getResponseCode();
if (status < 200 || status >= 300) {
sheet.getRange(row, imageColumn).setValue(
'Capture failed (HTTP ' + status + '): ' + response.getContentText().slice(0, 300)
);
continue;
}
const blob = response.getBlob().setName('website-row-' + row + '.png');
const image = sheet.insertImage(blob, imageColumn, row);
image.setWidth(480);
image.setHeight(300);
}
}
Apps Script supports HTTP requests with UrlFetchApp. For a GET request, the payload values are sent as query parameters. The response is handled as a blob so Sheets receives image data directly; no publicly accessible image URL is required. The example uses the ScreenshotNeo API base and the documented one-call request pattern. See the ScreenshotNeo docs for option names, output formats, and API behavior.
2. Authorize external requests
The first run prompts for authorization. Apps Script’s external request scope is https://www.googleapis.com/auth/script.external_request. If the project uses explicit scopes in its manifest, include that scope. The account running the script must also be allowed to authorize the project under its organization settings.
3. Arrange the sheet
Put full page URLs in column A, starting at A2. Run captureWebsitesInSheet from the Apps Script editor. It inserts images in column B; resize rows if you want more vertical room. Re-running the function can insert another image over an existing one, so remove or replace prior images if you want a clean refresh workflow.
cURL, Python, and Node.js alternatives
These are the same one-request capture in other environments. Save the response as an image, then insert or upload that image into Sheets. The cURL and Python examples save the response body directly; Node.js writes it to a file.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
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()
with open("shot.webp", "wb") as f:
f.write(r.content)
import { writeFile } from 'node:fs/promises';
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(`Screenshot request failed: HTTP ${res.status}`);
await writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
Keep API keys private, and avoid putting secrets in a sheet shared with readers. Query parameters may be recorded in logs by infrastructure you control; check your provider’s current security guidance. ScreenshotNeo’s parameters include image format and capture options; use its docs to select PNG, JPEG, WebP, PDF, or other settings appropriate to the sheet.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request can return a PNG, JPEG, WebP, or PDF. It handles consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
Use the returned image in Apps Script as a blob, as in the example above, or save and upload it to Sheets. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Every feature is available on every plan. See the API docs and sign up for 1,000 free screenshots a month, with no card.
Options and practical considerations
Pick the capture and output that fit the sheet
- Full page or viewport: Full-page captures suit page archives; viewport captures are smaller and quicker to review. Lazy-loaded content may need full-page handling or a wait option.
- Targeted content: Use an element selector when only one chart or panel matters. Hide selectors to remove page elements from the result.
- Format and size: PNG, JPEG, or WebP can be used for images; use PDF when a document output is needed. Set image dimensions in Sheets or resize the capture to keep a large sheet manageable.
- Dynamic pages: Use a wait-for-selector, delay, or network-idle setting where the page needs time to render. Authenticated pages may require cookies, headers, or authorization settings supported by the renderer.
- Repeatability: Use custom CSS or JavaScript, a fixed viewport or device preset, timezone, or geolocation when those affect the view you need to compare.
ScreenshotNeo supports full-page capture, CSS element capture, dark mode, device presets and custom viewports, retina scale, PDF settings, HTML/CSS rendering, custom CSS and JavaScript, clicks, selector hiding, wait conditions, request blocking, headers, cookies, user agent, authorization, timezone, geolocation, transparent backgrounds, resizing, caching with a chosen TTL, signed links, async jobs with signed webhooks, bulk capture up to 100 URLs per call, usage API, and an OpenAPI spec. Its parameter names also accept those used by other screenshot APIs to ease switching. Check the documentation for exact parameters and combinations.
Reliability, privacy, and cost
- Apps Script quotas: A script that loops through many URLs consumes execution time and external request quota. Process a manageable batch, record status per row, and resume from failed rows rather than retrying everything blindly.
- Caller network: Apps Script requests come from Google’s network infrastructure, whose source IPs come from a set pool. A destination that restricts caller IPs may reject the request.
- Access and privacy: The renderer receives the target URL and any supplied headers or cookies. Check the provider’s privacy and retention terms before sending confidential URLs or credentials. Avoid exposing API keys in sheet cells.
- Billing: The screenshot service may charge per capture according to its plan and billing rules. ScreenshotNeo bills only clean shots; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Its plans are Free (1,000/month), Starter ($5 for 3,000), Growth ($15 for 15,000), Pro ($39 for 60,000), Scale ($99 for 250,000), and Business ($249 for 1,000,000); yearly billing gives two months free.
- Sheet size: Many large images make a workbook slower to open and edit. Capture only what readers need, use appropriate dimensions, and consider retaining links or generating images on demand for large inventories.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
IMAGE() shows an error or blank result |
The URL is a webpage, inaccessible, not a direct image, or an unsupported Drive/SVG URL. | Use a direct supported image URL or call a browser renderer and insert its image output. |
IMPORTHTML() returns data but no screenshot |
The function imports a table or list, not a rendered visual page. | Use a screenshot renderer for a visual capture. |
| Apps Script authorization fails | The external request scope is missing or organization policy blocks authorization. | Authorize the script, add script.external_request to explicit scopes if used, or ask the Workspace administrator about policy. |
| HTTP error from the screenshot endpoint | Invalid API key, unsupported parameters, inaccessible target, or a provider-side response error. | Check the key and parameter names, inspect the HTTP response body and provider response headers, then retry only after addressing the cause. |
| Screenshot is blank or incomplete | The page has not rendered, relies on scripts or authentication, or blocks the renderer. | Set an appropriate wait condition, provide supported authentication, or inspect the page verdict and capture options. No renderer can guarantee access to every page. |
| Images overlap or rows look too short | Images are floating over the grid and row height is unchanged; a rerun may have added duplicates. | Resize rows and columns, choose in-cell placement for manual insertion where appropriate, and remove old floating images before refreshing. |
| Works manually, fails in automation | The script’s network origin, account permissions, or request quota differs from the browser session. | Check Apps Script execution logs, access scopes, external service restrictions, and current quotas. |
FAQ
Can I take a screenshot of a website from its URL in Google Sheets?
Yes, with automation that calls a browser-based screenshot API. A cell formula alone does not render the URL as a screenshot.
Can IMPORTHTML capture a webpage’s appearance?
No. It extracts a table or list from HTML; it does not preserve the page’s visual layout.
Can IMAGE() show a screenshot returned by an API?
It can display a supported, accessible image URL. If the API returns image bytes, use Apps Script to insert the blob into the sheet instead.
Will the capture include content behind a login?
Only if the renderer can access that content and you provide credentials through a supported, secure mechanism. Verify provider options and site access rules before sending credentials.
Does the screenshot update automatically when the website changes?
Not by itself. Run the script again or schedule an authorized Apps Script trigger, and decide how the refresh should remove or replace existing images.


