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

Run html2canvas(element) in the browser, export the resulting canvas, then POST it to a Python endpoint. For small images, send a PNG data URL in JSON; for larger images, use canvas.toBlob() with FormData. The examples below use Flask and include checks for invalid data and oversized uploads.

What html2canvas produces—and what it does not

html2canvas(element) runs in the browser and resolves to an HTML <canvas>. It does not create a file on the Python server or transmit anything by itself. Your JavaScript must export the canvas and send the exported data in an HTTP request; Python then validates and stores or processes that upload.

Also, html2canvas reconstructs an image from DOM and CSS information it supports. It is not a pixel-perfect capture of the browser’s rendered pixels, so unsupported CSS or rendering differences can affect the result. See the html2canvas documentation for its rendering model and limitations.

Choose JSON/base64 or Blob/FormData

Method Browser representation Useful when Trade-off
JSON with data URL canvas.toDataURL("image/png") You want a straightforward endpoint and relatively small screenshots. Base64 encodes binary data, increasing the payload and requiring encoding/decoding work.
Multipart form upload canvas.toBlob() appended to FormData You expect larger screenshots or want to send the image as binary. You handle a Blob and a multipart upload rather than a JSON string.

Flask’s documentation notes that JSON cannot represent binary data directly, so base64 takes more bandwidth, can be slow, and is less convenient to cache: Flask: JavaScript and Fetch. For larger images, use the Blob/FormData option below.

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

Option 1: Send a PNG data URL as JSON

This complete example captures the element with ID capture, converts it to PNG, and posts a JSON object to Flask. Serve the page from the same application origin, or configure your server’s CORS policy if the API has a different origin.

Browser JavaScript

<script type="module">
  import html2canvas from "https://cdn.jsdelivr.net/npm/[email protected]/+esm";

  async function sendScreenshot() {
    const element = document.querySelector("#capture");
    if (!element) throw new Error("Could not find #capture");

    const canvas = await html2canvas(element, { backgroundColor: "#fff" });
    const dataUrl = canvas.toDataURL("image/png");

    const response = await fetch("/api/screenshot", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ image: dataUrl })
    });
    if (!response.ok) throw new Error(`Upload failed: ${response.status}`);
    return response.json();
  }
</script>

Flask endpoint

from base64 import b64decode
from binascii import Error as Base64Error
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.post("/api/screenshot")
def receive_screenshot():
    payload = request.get_json(silent=False)
    if not isinstance(payload, dict):
        return jsonify(error="expected a JSON object"), 400

    data_url = payload.get("image", "")
    prefix = "data:image/png;base64,"
    if not isinstance(data_url, str) or not data_url.startswith(prefix):
        return jsonify(error="expected a PNG data URL"), 400

    try:
        image_bytes = b64decode(data_url[len(prefix):], validate=True)
    except (Base64Error, ValueError):
        return jsonify(error="invalid base64"), 400

    if len(image_bytes) > 10 * 1024 * 1024:
        return jsonify(error="image too large"), 413

    with open("upload.png", "wb") as output:
        output.write(image_bytes)
    return jsonify(ok=True, bytes=len(image_bytes))

The response is JSON such as {"ok":true,"bytes":12345}; the byte count depends on the captured content. This sample overwrites upload.png on each request. In a real application, authenticate callers, choose a per-user or unique storage name, and store files in an appropriate location rather than a shared fixed path.

Option 2: Upload a Blob with FormData

toBlob() creates binary image data without first making a base64 string. The browser creates the multipart boundary automatically, so do not manually set the request’s Content-Type header.

Browser JavaScript

async function uploadScreenshot() {
  const element = document.querySelector("#capture");
  if (!element) throw new Error("Could not find #capture");

  const canvas = await html2canvas(element, { backgroundColor: "#fff" });
  const blob = await new Promise(resolve => canvas.toBlob(resolve, "image/png"));
  if (!blob) throw new Error("canvas export failed");

  const form = new FormData();
  form.append("screenshot", blob, "screenshot.png");

  const response = await fetch("/api/screenshot-upload", {
    method: "POST",
    body: form
  });
  if (!response.ok) throw new Error(`Upload failed: ${response.status}`);
  return response.json();
}

Flask endpoint

from flask import request, jsonify

@app.post("/api/screenshot-upload")
def receive_upload():
    uploaded = request.files.get("screenshot")
    if uploaded is None or uploaded.mimetype != "image/png":
        return jsonify(error="PNG upload required"), 400

    image_bytes = uploaded.read()
    if len(image_bytes) > 10 * 1024 * 1024:
        return jsonify(error="image too large"), 413

    with open("upload.png", "wb") as output:
        output.write(image_bytes)
    return jsonify(ok=True, bytes=len(image_bytes))

In production, avoid trusting a client-provided filename or MIME type as proof of file contents. Apply request-size limits at the web server and application layers, validate the image format, and use safe storage names. Add authentication and authorization appropriate to your application; neither example is an access-control system.

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

Cross-origin images, blank captures, and canvas security

A page can display an image from another origin while still being unable to export that image through canvas. If the image server does not permit cross-origin access, drawing it taints the canvas; calling toDataURL() or toBlob() can then fail with a security error. html2canvas’s configuration includes useCORS, but enabling it only requests CORS-enabled image loading—it cannot override the image server’s policy.

  • Set useCORS: true when the image host returns suitable CORS response headers.
  • If you control the image delivery, configure its response to allow the page’s origin.
  • If you do not control that host, serve the image through a same-origin proxy that you operate and are authorized to use.
  • Check browser developer tools for failed image requests and CORS messages, then test exporting the canvas after the image has loaded.
const canvas = await html2canvas(element, { useCORS: true });

useCORS is not a way to bypass access controls. A proxy also needs appropriate validation and protections; do not expose an unrestricted proxy that fetches arbitrary URLs.

Control clipping, dimensions, and output quality

If only part of a long element appears, set the rendering window dimensions to the element’s scroll dimensions. The option names are windowWidth and windowHeight; use the element’s scrollWidth and scrollHeight where appropriate:

const canvas = await html2canvas(element, {
  windowWidth: element.scrollWidth,
  windowHeight: element.scrollHeight,
  backgroundColor: "#fff"
});

For higher-density output, set scale to window.devicePixelRatio. A larger scale produces more pixels and can consume more memory and processing time, and the resulting upload may be larger.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const canvas = await html2canvas(element, {
  scale: window.devicePixelRatio,
  backgroundColor: "#fff"
});

These options affect rendering and export size, not what Python can receive. If the endpoint returns HTTP 413, lower the capture dimensions or scale, or choose an upload limit suitable for your application. Do not simply raise limits without considering available storage and request handling.

Or skip the browser setup

If your goal is a server-side screenshot of a URL rather than an image of the current user’s DOM state, ScreenshotNeo can return an image from one GET request. The JavaScript-to-Python examples above remain the right approach when the capture must reflect content rendered in the user’s browser.

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

See the ScreenshotNeo API documentation for request options and response details. ScreenshotNeo accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; those steps can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Learn more at ScreenshotNeo, or sign up for 1,000 free screenshots a month with no card.

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

Troubleshooting the upload

Symptom Likely cause What to check
Could not find #capture The selector is wrong or the capture runs before the element exists. Confirm the element ID and call the capture function after the page has rendered it.
Canvas export throws a security error A cross-origin image tainted the canvas. Check image response headers, use useCORS: true only when supported, or use a controlled same-origin proxy.
Capture is blank or misses images Images may not have loaded, may be blocked, or may be inaccessible due to CORS. html2canvas may also lack support for a rendered detail. Inspect browser console and network requests; verify image loading and CORS, then compare the DOM/CSS used with the documented supported rendering behavior.
Only the visible portion appears The capture dimensions do not cover the element’s full scroll area. Pass suitable windowWidth and windowHeight values based on scrollWidth and scrollHeight.
canvas export failed in the Blob example toBlob() returned null. Check canvas rendering and browser console errors; do not submit a missing Blob.
HTTP 400 from Flask The JSON data URL prefix, image field, or multipart field/type is not what the endpoint expects. For JSON, send a string beginning data:image/png;base64,. For multipart, use the field name screenshot and PNG output.
HTTP 413 The upload exceeds the endpoint or hosting platform’s size limit. Reduce dimensions or scale, or deliberately adjust the applicable request limits.
Fetch reports a network error The route may be incorrect, the server may be unavailable, or a cross-origin request may lack CORS permission. Inspect the request URL, server logs, and browser network panel; configure CORS for the API origin if the page and API are on different origins.

Practical reliability and cost considerations

Capture and upload are separate failure points: html2canvas must finish rendering, the browser must export the image, and the server must accept and store it. Catch errors around the capture and fetch calls, provide a user-visible failure state, and avoid treating a successful HTTP response as proof of durable storage unless your endpoint confirms that explicitly.

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

Large dimensions and high pixel scale increase browser memory use, processing time, and transfer size. Blob/FormData avoids base64’s payload expansion and encoding overhead, making it the more practical choice for larger uploads. For small captures or easy inspection during development, JSON may be simpler. Enforce a size cap on the server either way, and account for the hosting platform’s own request limits.

Frequently Asked Questions

Can JavaScript send a screenshot directly to Python without saving a file first?

Yes. Export the canvas to a data URL or Blob in memory and POST it; the browser does not need to download a local file first.

Does html2canvas capture the exact browser pixels?

No. It reconstructs the image from DOM and CSS information it understands, so its output can differ from the browser’s rendered appearance.

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.

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.