The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Direct answer: use Selenium’s driver.get_screenshot_as_png() to obtain the current browser window as PNG bytes, then bind those bytes to a parameter in an INSERT statement targeting a binary SQL column. The example below uses Python, Selenium and Microsoft SQL Server’s mssql-python driver. Store identifiers and image metadata with the bytes so each screenshot can be found, validated and interpreted later.
What the workflow stores
A screenshot is binary data, not SQL text. The reliable sequence is:
- Navigate to the page and wait until the state your test is meant to preserve is present.
- Capture the current browsing context with Selenium.
- Insert the returned bytes and metadata through bound parameters.
- Commit the transaction.
- Fetch the binary value later and write it with binary file mode when a PNG is needed.
Selenium’s Python WebDriver API describes get_screenshot_as_png() as: “Gets the screenshot of the current window as a binary data.” The method returns Python bytes, so no Base64 decoding is needed before inserting into a binary column.
Prerequisites and stack used here
- Python with Selenium 4.x, a supported browser and its WebDriver.
- Microsoft SQL Server and the
mssql-pythondriver. - A database login that can create tables and insert, select and update rows.
- A page-specific wait condition. A screenshot taken before a modal closes or data finishes rendering is still a valid screenshot, but probably the wrong test artifact.
The SQL type and parameter-marker syntax are not universal. The code in this section is explicitly for SQL Server with Microsoft’s Python driver; adapt the markers and connection code for another driver.
#1 Best Overall
Create a table for image bytes and metadata
SQL Server supports variable-length binary columns. Microsoft documents varbinary(n) up to 8,000 bytes and varbinary(max) up to 2 GB. A screenshot table can therefore use varbinary(max) when image sizes are not tightly bounded. The older image type is a deprecated legacy type and should not be chosen for new work.
CREATE TABLE dbo.SeleniumScreenshots (
ScreenshotId bigint IDENTITY(1,1) PRIMARY KEY,
TestRunId nvarchar(100) NOT NULL,
PageUrl nvarchar(2048) NOT NULL,
CapturedAtUtc datetime2(7) NOT NULL,
FileName nvarchar(260) NULL,
ContentType nvarchar(100) NOT NULL,
FileSizeBytes bigint NOT NULL,
ImageWidth int NULL,
ImageHeight int NULL,
Description nvarchar(1000) NULL,
ImageData varbinary(max) NULL
);
ImageData is nullable so an application can represent an absent capture as SQL NULL. Do not confuse that with a legitimate zero-length byte value; your application should define how each state is handled.
The metadata columns are practical rather than decorative: a test or run identifier supports lookup, the URL identifies the page, UTC capture time orders events, content type tells consumers how to interpret the bytes, and the stored size makes growth and integrity checks measurable. If dimensions matter to your reports, populate them from the image-processing layer and leave them nullable when they are not available.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Capture and insert a screenshot with Python
The following complete example opens a page, waits for a selector, captures PNG bytes, and inserts them with a parameterized statement. Replace the connection details and selector with values for your environment.
from datetime import datetime, timezone
from uuid import uuid4
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from mssql_python import connect
TARGET_URL = "https://example.com"
TEST_RUN_ID = str(uuid4())
# Configure the browser according to your test environment.
options = webdriver.ChromeOptions()
# options.add_argument("--headless=new")
driver = webdriver.Chrome(options=options)
try:
driver.get(TARGET_URL)
# Use a condition that represents the state you intend to archive.
WebDriverWait(driver, 30).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "body"))
)
png_bytes = driver.get_screenshot_as_png()
captured_at = datetime.now(timezone.utc)
connection = connect(
"Server=localhost;Database=TestArtifacts;"
"User Id=app_user;Password=YOUR_PASSWORD;"
"Encrypt=yes;TrustServerCertificate=yes"
)
try:
cursor = connection.cursor()
cursor.execute(
"""
INSERT INTO dbo.SeleniumScreenshots
(TestRunId, PageUrl, CapturedAtUtc, FileName,
ContentType, FileSizeBytes, Description, ImageData)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""",
(
TEST_RUN_ID,
TARGET_URL,
captured_at,
f"{TEST_RUN_ID}.png",
"image/png",
len(png_bytes),
"Selenium browser-window capture",
png_bytes,
),
)
connection.commit()
finally:
connection.close()
finally:
driver.quit()
The driver’s parameter marker is shown as ? for this SQL Server example. Keep the values separate from the SQL string; concatenating binary data or URLs into SQL text creates quoting, corruption and injection problems.
Retrieve the bytes and recreate a PNG
A binary column returns bytes through the driver. Write them with "wb", not text mode.
from mssql_python import connect
connection = connect(
"Server=localhost;Database=TestArtifacts;"
"User Id=app_user;Password=YOUR_PASSWORD;"
"Encrypt=yes;TrustServerCertificate=yes"
)
try:
cursor = connection.cursor()
cursor.execute(
"""
SELECT FileName, ContentType, ImageData
FROM dbo.SeleniumScreenshots
WHERE ScreenshotId = ?
""",
(123,)
)
row = cursor.fetchone()
if row is None:
raise LookupError("Screenshot was not found")
file_name, content_type, image_data = row
if image_data is None:
raise ValueError("The row contains SQL NULL, not image bytes")
with open(file_name or "restored.png", "wb") as output:
output.write(image_data)
finally:
connection.close()
For validation, inspect the data itself rather than trusting only FileName or ContentType. A PNG normally begins with the PNG signature bytes 89 50 4E 47 0D 0A 1A 0A. Microsoft’s SQL Server guidance recommends magic-byte validation for file formats; apply equivalent checks for JPEG or GIF when those formats are used.
Rank #2
Choose the right Selenium capture scope
Current browser window
driver.get_screenshot_as_png() captures the current window and returns PNG bytes. It is the straightforward choice for the SQL example above.
One element
When the artifact should contain only a chart, form, assertion panel or other component, locate the element and use the element screenshot method exposed by your Selenium binding. This avoids storing unrelated page pixels and makes later comparisons more focused.
element = driver.find_element(By.CSS_SELECTOR, "#checkout-summary")
element_png = element.screenshot_as_png
Save directly to a file
driver.save_screenshot(path) and driver.get_screenshot_as_file(path) write PNG files. The Selenium API expects a path ending in .png; file methods report False when an I/O error occurs. These methods are useful for a local fallback, but they are not necessary when the database is the destination.
Base64 output
driver.get_screenshot_as_base64() returns a Base64 string and is documented as useful for embedding an image in HTML. For a binary SQL column, the bytes-returning method avoids storing an encoded representation and the extra decode step.
Free tools Windows power users keep installed
One-click scans. No signup required.
Full-page expectations
A screenshot is not automatically a full-page capture in every browser, driver or binding. WebDriver implementations can differ in whether they capture the entire page, the current window, a visible frame or another best-effort region. Verify the exact browser, driver and method if full-page coverage is a requirement; do not infer it from a successful call alone.
PostgreSQL and other SQL engines
PostgreSQL documents bytea for binary strings, so the same PNG bytes can be stored in a bytea column. The table definition and parameter placeholders depend on the PostgreSQL driver you select. Confirm that driver’s binary-binding rules before adapting the insert; do not copy SQL Server’s varbinary(max) type or ? markers into PostgreSQL unchanged.
For any engine, the invariant is the same: use the engine’s binary type, bind the Python byte value as a parameter, commit explicitly according to the driver’s transaction behavior, and retain enough metadata to identify the capture.
Rank #3
When SQL storage is a good fit
Microsoft’s SQL Server guidance favors database storage when files are small (its example guidance uses under 1 MB), when transactional consistency with related records matters, or when screenshots should be backed up together with database data. Those are workload recommendations, not a universal cutoff. Measure your actual PNG sizes, capture rate and backup window.
| Requirement | Likely fit | Reason |
|---|---|---|
| Small artifacts tightly tied to a test result | SQL binary column | One transaction can preserve the result and screenshot together. |
| Large files or very high capture volume | Filesystem or object storage | Reduces database growth and may simplify large-object delivery. |
| Direct downloads or CDN delivery | Filesystem/object storage | Clients can fetch the file without a database round-trip. |
| Transactional consistency with filesystem-scale data | SQL Server FILESTREAM | Microsoft describes FILESTREAM as filesystem storage with transactional consistency; it requires server-side configuration. |
Also compare backup and restore time, retention, access controls, operational complexity and whether downstream clients need a stable file URL. Protect screenshots under the same backup and security policy as the test data they document.
Reliability and data-integrity practices
- Wait for intent, not merely page load: wait for the selector, state or network condition that defines a meaningful test checkpoint.
- Capture once per checkpoint: give each row a run ID and description so retries do not create ambiguous duplicates.
- Keep transactions short: capture in the browser, then open the database transaction for the insert and commit promptly.
- Record measured size: store
len(png_bytes)and monitor database growth against retention rules. - Validate content: check magic bytes and, where needed, decode the image before treating it as a valid artifact.
- Handle failures explicitly: a browser timeout, a database rollback and a missing screenshot are different events and should have different status or logging paths.
Common errors and fixes
The screenshot is blank or shows the wrong state
The capture ran before the page finished rendering, a lazy image loaded, or a modal covered the content. Add a wait for the application’s real readiness condition, scroll or interact as the test requires, and capture after that condition—not after an arbitrary sleep unless a fixed delay is genuinely what the application needs.
Only the visible viewport was saved
Current-window capture does not guarantee a full-page image. Check the documented behavior of your browser and driver, or capture the specific element that represents the required artifact. If complete page coverage is mandatory, test it on the exact browser/driver versions used in CI.
SQL reports a type or parameter error
Verify that the target column is a binary type and that the driver’s parameter markers match the statement. The example uses SQL Server and mssql-python; another engine may require different markers or binary adaptation.
The inserted image cannot be opened
Ensure the value was bound as bytes, not converted to text or Base64 accidentally. Retrieve it as bytes, write with "wb", and inspect the file signature. Check that the transaction committed and that no application layer truncated the value.
A row contains NULL
None maps to SQL NULL in the documented Python driver. Decide whether a failed capture should produce no row, a row with NULL, or a separate failure record; do not silently treat NULL as a valid image.
Rank #4
Temporary objects reject the binary parameter
Microsoft notes that temporary-table or table-variable metadata can require explicit input sizing in this driver. If the same insert works against a permanent table but fails for a temporary object, inspect the driver’s input-size configuration and the temporary column metadata.
Or skip the browser setup
If you need a rendered website image rather than a Selenium-managed test session, ScreenshotNeo provides a single HTTP request. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server lets Claude, Cursor and other MCP clients call take_screenshot, get_page_info and capture_pdf.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Save the returned image bytes to SQL using the same parameterized insert shown above:
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 bytes = Buffer.from(await res.arrayBuffer());
See the ScreenshotNeo API documentation for options such as selectors, full-page capture, device presets, custom waits, headers, cookies, JavaScript, PDF output, caching, asynchronous jobs and bulk capture. 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 to try it.
FAQ
Should I store Base64 instead of PNG bytes?
Usually no. Base64 is useful when embedding an image in HTML, while a binary SQL column is designed for the original bytes. Store Base64 only when a text-oriented interface specifically requires it.
Can I save JPEG or WebP instead?
The Selenium method used here returns PNG. Converting to another format requires an image-processing step and a matching content type; do not label converted data as image/png.
Does committing the row prove that the screenshot is visually correct?
No. A successful database transaction proves that bytes and metadata were stored. Visual correctness still depends on page readiness, browser state and the capture scope you selected.
How should I prevent unbounded screenshot growth?
Measure real sizes and capture volume, define retention or archival rules, and include the binary column in capacity and backup planning. Move large or delivery-oriented artifacts to filesystem or object storage when that better matches the workload.
Quick Recap
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.

