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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Capture a screenshot as an image, encode it into bytes, split those bytes into explicitly sized pieces, and send each piece using an upload protocol that the receiving server supports. The example below uses 1,024 bytes per chunk—one binary kibibyte, often informally called “1 KB.” The final chunk can be shorter. Splitting bytes in Python does not, by itself, tell a server how to reassemble the image.

What “1 KB chunks” means

There are two common interpretations of KB: 1,000 bytes (decimal) and 1,024 bytes (binary, technically one kibibyte or KiB). This tutorial chooses 1,024 bytes. If your receiving service specifies 1,000-byte parts, set CHUNK_SIZE = 1000 instead. What matters is that the sender and receiver use the same size and protocol.

These are application-level chunks: slices of the screenshot’s encoded byte sequence. They are different from HTTP chunked transfer encoding, which frames a request body for transport. HTTP transport framing does not guarantee that the receiver will see application pieces of exactly 1,024 bytes.

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

Capture and encode a screenshot

Install Pillow if it is not already available in your Python environment:

python -m pip install Pillow

Pillow’s ImageGrab.grab() captures the whole screen when called without a bounding box. Saving the returned image to BytesIO encodes it in memory, so the example does not need a temporary screenshot file. PNG is used here; change the format only if the receiving endpoint supports it.

from io import BytesIO
from PIL import ImageGrab

image = ImageGrab.grab()
buffer = BytesIO()
image.save(buffer, format="PNG")
screenshot_bytes = buffer.getvalue()

print(f"Encoded screenshot: {len(screenshot_bytes)} bytes")

The image object is not the upload body. Serialization produces the bytes that can be sliced and transmitted. Pillow documents platform differences: the returned image may use RGB or RGBA, macOS Retina capture can affect dimensions, and Linux may use fallback utilities if the default X11 display cannot return a snapshot. See the Pillow ImageGrab documentation for capture behavior and options.

Split the encoded bytes into fixed-size pieces

Use offsets to slice the complete encoded byte string. The last slice is shorter whenever the total byte count is not an exact multiple of the selected size.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
CHUNK_SIZE = 1024  # bytes: one binary kibibyte

chunks = [
    screenshot_bytes[start:start + CHUNK_SIZE]
    for start in range(0, len(screenshot_bytes), CHUNK_SIZE)
]

print(f"Chunks: {len(chunks)}")
print(f"First chunk: {len(chunks[0]) if chunks else 0} bytes")
print(f"Last chunk: {len(chunks[-1]) if chunks else 0} bytes")

For a large screenshot, avoid retaining a second list of all pieces. Iterate over offsets instead, which keeps only the current slice in addition to the original encoded bytes:

for part_index, start in enumerate(range(0, len(screenshot_bytes), CHUNK_SIZE)):
    chunk = screenshot_bytes[start:start + CHUNK_SIZE]
    # Send chunk using the receiver's documented upload contract.

Each chunk is a byte string, not a complete image. It may begin or end in the middle of a PNG structure; that is expected. Only the concatenated bytes in their original order form the encoded screenshot.

Send parts only according to the receiver’s protocol

There is no universal endpoint or header for uploading numbered screenshot parts. The receiving API must specify how to start an upload, where each part goes, how it identifies the image and part order, and how to finish or commit the upload. It may require an upload ID, part number, checksum, authentication, or a finalization request; include those only when the service documents them.

The example below is deliberately a template, not a runnable upload request: replace the URL, method, headers, and part fields with the exact contract from your server. A generic HTTP POST to an invented route cannot guarantee that the server will accept or reconstruct the screenshot.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
# Pseudocode: adapt to the receiving API's documented contract.
for part_index, start in enumerate(range(0, len(screenshot_bytes), CHUNK_SIZE)):
    chunk = screenshot_bytes[start:start + CHUNK_SIZE]
    response = send_part(
        upload_id=upload_id,
        part_number=part_index,
        data=chunk,
    )
    response.raise_for_status()

finalize_upload(upload_id)

Use the response and retry rules documented by the endpoint. If a request fails, do not blindly resend a part unless the API says repeated part uploads are safe or provides an idempotency mechanism. Python’s generic HTTP iterable support is not a resumable-upload protocol.

Application chunks versus HTTP chunked transfer encoding

Python HTTP clients can accept iterables of bytes as request bodies. When framing headers are omitted, those clients may choose HTTP chunked transfer encoding for an iterable body. That handles transport framing, not fixed-size application parts or image reassembly.

Approach What it controls What it does not define
Explicit byte slicing The byte boundaries and size of pieces your application hands to the upload logic. How the server identifies, orders, validates, stores, or combines the pieces.
HTTP chunked transfer encoding Transport framing of a request body when supported and selected by the client and server. Application-level 1,024-byte boundaries, upload IDs, part numbering, or resumability.

For exact pieces, send each slice through the receiving API’s per-part operation. If the endpoint instead expects one streaming request body, use the framing its documentation requires and do not describe transport frames as fixed 1 KB screenshot parts.

The Python documentation states for http.client: “If body is an iterable, the elements of the iterable are sent as is until the iterable is exhausted.” That behavior does not define the receiver’s application-level upload contract. See Python’s http.client documentation and urllib.request documentation. For the in-memory buffer used above, see Python’s io documentation.

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

Memory, format, and reliability considerations

  • Encoded size, not screen dimensions, determines chunk count. Compression, screen contents, and image format affect the byte length. Compute the count from the encoded data, not from pixel dimensions.
  • Streaming reduces duplicate memory. The offset loop avoids constructing a list containing every slice, but the example still holds the full screenshot bytes in memory because BytesIO.getvalue() returns the complete buffer.
  • Choose the image format the receiver accepts. PNG is lossless; other formats may encode differently and have different size and quality characteristics. Do not switch formats without confirming the endpoint supports them.
  • Validate reassembly when the protocol supports it. A receiver may verify the completed object with a checksum or size. Follow its documented mechanism rather than inventing metadata.
  • Plan for retries at the protocol level. A failed part request can leave an incomplete upload. Use documented retry, status-check, abort, and finalize operations if provided; generic Python slicing supplies none of these.
  • Keep credentials out of source control. If the endpoint requires a token, pass it using the authentication method it documents and avoid printing it in logs.

Troubleshooting screenshot capture and uploads

The screenshot is blank or capture raises an error

Capture behavior depends on the operating system, active display, permissions, and display server. On Linux, check whether the process has access to the intended display and whether the fallback capture utilities required for your environment are installed. On macOS, review screen-recording permissions and confirm the captured dimensions are what you expect.

The server rejects a piece as too large or too small

Check whether the service means 1,000 or 1,024 bytes and whether its stated size limit applies to each part or to the entire request. Confirm the server’s contract allows a shorter final part; if it requires a different finalization scheme, implement that exact behavior.

The reconstructed file cannot be opened

Verify that all parts were delivered once as required, in the correct order, and without text encoding or other transformations. Reassemble binary data as bytes, not decoded text. Compare the resulting length or checksum with the sender’s value if the API supports such checks.

The upload stalls or fails midway

Inspect the endpoint’s response codes and documented timeout and retry behavior. Determine whether it preserves accepted parts, requires a new upload ID, or supports querying upload status. Do not assume a failed request can safely be repeated without checking whether the server treats duplicate part numbers as replacements or errors.

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

HTTP chunked requests are refused

The receiving server or intermediary may not support chunked transfer encoding, or the endpoint may require a known content length. Follow its framing requirement. If it wants numbered application parts, send each part as a separate request instead of relying on transport chunking.

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 goal is a screenshot of a web page rather than the current desktop, ScreenshotNeo can return a page capture through a single GET request. It offers PNG, JPEG, WebP, or PDF output; its API is not a generic protocol for splitting a local desktop image into 1 KB upload parts.

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. It removes cookie and consent banners, newsletter popups, and chat widgets before capture, with each cleanup step configurable. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; responses indicate the page verdict and billing status in headers. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Sign up free for 1,000 screenshots a month—no card required.

Frequently Asked Questions

Will the last chunk always contain 1,024 bytes?

No. It contains the remaining bytes and is shorter when the total encoded screenshot size is not an exact multiple of 1,024.

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

Does HTTP chunked transfer encoding mean the image is being uploaded in 1 KB pieces?

No. It frames the request body at the transport level; it does not guarantee application-visible 1,024-byte pieces.

Can I use this exact upload loop with any server?

No. The server must document an endpoint and contract for receiving and reassembling the parts.

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.