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.

Use page.expect_download() around the action that starts the download, obtain the resulting Download object, and call download.save_as() with a path inside a Python temporary directory. This gives your program an explicit, predictable destination while keeping cleanup under your control.

The reliable download workflow

Playwright downloads are event-driven. Enter the download-waiting context before clicking the link or button that triggers the attachment. Then save the completed (or still-transferring) download to a path you choose.

  1. Create a temporary directory with Python’s TemporaryDirectory.
  2. Launch Playwright and create a browser context and page.
  3. Navigate to the page containing the download control.
  4. Wrap the triggering action in page.expect_download().
  5. Read the resulting Download object.
  6. Call download.save_as() with a path in the temporary directory.
  7. Consume the file before the temporary-directory context exits, or move it elsewhere if it must survive.

Microsoft’s Downloads | Playwright Python documentation says attachments are downloaded into a temporary folder and that files belonging to a browser context are deleted when that context closes. An explicit copy made with save_as() has the lifetime of the destination you select.

Free tools Windows power users keep installed

One-click scans. No signup required.

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

Synchronous Python example

This complete script uses a deterministic filename. Replace the URL and locator with the page and control in your application.

#1 Best Overall
Lexar D40E 128GB Dual USB 3.2 Gen 1 Type-C Jump Drive, Champagne Silver
  • USB-C 2-in-1 storage OTG: The Lexar JumpDrive Dual Drive D40E features USB Type-A and Type-C connectors in a slim, portable form factor for easy device compatibility
  • Transfer speeds up to 100MB/s: Based on internal testing, performance may vary depending upon the host device, interface, and usage conditions. 1MB=1,000,000 bytes
  • Plug and Play: Widely compatible with USB Type-C smartphones, tablets, laptops, Macs, and traditional Type-A devices, no software installation required. The 360° swivel design allows for easy switching between connectors without the hassle of losing a cap
  • Durable & Compact: The Lexar D40E USB memory stick features a metal enclosure, withstands temperatures from 0° to 50° C (32°F to 122°F), and is lightweight at 26g with dimensions of 70.4 x 16.9 x 11.7mm
  • Security & Warranty: Securely protects files using an advanced security software solution with 256-bit AES encryption. Backed by a Lexar 3-year limited warranty
from pathlib import Path
from tempfile import TemporaryDirectory
from playwright.sync_api import sync_playwright

with TemporaryDirectory() as temp_dir:
    destination = Path(temp_dir) / "report.csv"

    with sync_playwright() as p:
        browser = p.chromium.launch()
        context = browser.new_context()
        page = context.new_page()
        page.goto("https://example.com")

        with page.expect_download() as download_info:
            page.get_by_text("Download file").click()

        download = download_info.value
        download.save_as(destination)

        # Process the file while TemporaryDirectory is still active.
        print(destination, destination.stat().st_size)
        context.close()
        browser.close()

    # destination is removed when the TemporaryDirectory block ends.

The download event must be registered before the click. If the listener is installed afterward, a fast download can already have fired and your code can time out.

Asynchronous Python version

Use the async API consistently: async_playwright, async with, and await. Do not mix synchronous objects with async ones.

import asyncio
from pathlib import Path
from tempfile import TemporaryDirectory
from playwright.async_api import async_playwright

async def main():
    with TemporaryDirectory() as temp_dir:
        destination = Path(temp_dir) / "report.csv"

        async with async_playwright() as p:
            browser = await p.chromium.launch()
            context = await browser.new_context()
            page = await context.new_page()
            await page.goto("https://example.com")

            async with page.expect_download() as download_info:
                await page.get_by_text("Download file").click()

            download = await download_info.value
            await download.save_as(destination)
            print(destination, destination.stat().st_size)

            await context.close()
            await browser.close()

asyncio.run(main())

The temporary directory remains available for the entire with TemporaryDirectory() block. Read, parse, upload, or copy the file during that block. To retain it after cleanup, move it to a permanent directory before leaving the block.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
SANDISK 128GB Ultra Flair, USB-A Flash Drive, Up to 150MB/s Read Speeds
  • High-speed USB 3.0 performance of up to 150MB/s(1) [(1) Write to drive up to 15x faster than standard USB 2.0 drives (4MB/s); varies by drive capacity. Up to 150MB/s read speed. USB 3.0 port required. Based on internal testing; performance may be lower depending on host device, usage conditions, and other factors; 1MB=1,000,000 bytes]
  • Transfer a full-length movie in less than 30 seconds(2) [(2) Based on 1.2GB MPEG-4 video transfer with USB 3.0 host device. Results may vary based on host device, file attributes and other factors]
  • Transfer to drive up to 15 times faster than standard USB 2.0 drives(1)
  • Sleek, durable metal casing
  • Easy-to-use password protection for your private files(3) [(3)Password protection uses 128-bit AES encryption and is supported by Windows 7, Windows 8, Windows 10, and Mac OS X v10.9 plus; Software download required for Mac, visit the SanDisk SecureAccess support page]

Choosing a filename safely

Supply your own name

A fixed name such as report.csv is safest for automation. It avoids collisions caused by server-provided names and prevents page-controlled text from becoming an unsafe path. Add an identifier or create a unique temporary directory when several downloads run concurrently.

Use the suggested filename when appropriate

download.suggested_filename is generally derived from the response’s Content-Disposition header or the link’s download attribute. Browsers can compute this differently. If you use it, treat it as untrusted input: strip directory components, reject unexpected characters, and resolve it beneath your temporary directory rather than concatenating arbitrary path text.

from pathlib import Path

name = Path(download.suggested_filename).name
safe_destination = Path(temp_dir) / name
download.save_as(safe_destination)

For the API details and filename behavior, see the Download | Playwright Python reference.

Rank #3
2 Pack 64GB USB Flash Drive USB 2.0 Thumb Drives Jump Drive Fold Storage Memory Stick Swivel Design - Black
  • What You Get - 2 pack 64GB genuine USB 2.0 flash drives, 12-month warranty and lifetime friendly customer service
  • Great for All Ages and Purposes – the thumb drives are suitable for storing digital data for school, business or daily usage. Apply to data storage of music, photos, movies and other files
  • Easy to Use - Plug and play USB memory stick, no need to install any software. Support Windows 7 / 8 / 10 / Vista / XP / Unix / 2000 / ME / NT Linux and Mac OS, compatible with USB 2.0 and 1.1 ports
  • Convenient Design - 360°metal swivel cap with matt surface and ring designed zip drive can protect USB connector, avoid to leave your fingerprint and easily attach to your key chain to avoid from losing and for easy carrying
  • Brand Yourself - Brand the flash drive with your company's name and provide company's overview, policies, etc. to the newly joined employees or your customers

Temporary-directory lifetime and cleanup

TemporaryDirectory removes its directory when its context manager exits, including normal completion. That is useful for sensitive or intermediate artifacts, but it means this will fail after the block:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
with TemporaryDirectory() as temp_dir:
    path = Path(temp_dir) / "data.bin"
    download.save_as(path)

# The directory and path no longer exist here.

If another component needs the file later, copy it before cleanup:

import shutil
from pathlib import Path
from tempfile import TemporaryDirectory

permanent = Path("./processed")
permanent.mkdir(exist_ok=True)
with TemporaryDirectory() as temp_dir:
    temporary_path = Path(temp_dir) / "data.bin"
    download.save_as(temporary_path)
    shutil.copy2(temporary_path, permanent / "data.bin")

Playwright’s own managed download is removed when the producing browser context closes, even when you configure a launch directory. An explicit save_as() copy is therefore the appropriate boundary when your application needs a known destination.

Rank #4
SIMMAX 32GB Memory Stick USB 2.0 Flash Drives Swivel Thumb Drive Pen Drive (32GB Purple)
  • GOOD VALUE PACKAGE - 1 Pack 32GB Memory Stick USB 2.0 Flash Drives with great cost performance and high quality.
  • BIG CAPACITY - The available capacity: 29.10GB-29.8GB, You can save the data of movies, music, photos, designs, programs, manuals, handouts in a high speed.Good performance in digital data storing, transferring and sharing with families, friends, workmates, clients and machines.
  • EASY TO USE & PLUG AND WORK - Support windows 7 / 8 / 10 / Vista / XP / 2000 / ME / NT Linux and Mac OS, Compatible with USB2.0 and below.
  • TWISTTURN DESIGN & EASY CARRY - The metal clip rotates 360° round the ABS plastic body which with rubber oil skin feeling finish. The capless design can avoid lossing of cap, and providing efficient protection to the USB port.
  • WARRANTY & SUPPORT - SIMMAX logo is laser printed on the USB connector surface, our products are of good quality and we promise that any problem about the product within one year since you buy.

save_as(), path(), and downloads_path

Approach What it controls Important limitation Best use
download.save_as(path) Copies one download to your chosen path The destination still follows your directory’s cleanup rules An application-managed temporary file, deterministic name, or handoff to another process
download.path() Returns Playwright’s managed file path after a successful transfer Throws when connected remotely; the filename is a random GUID and context cleanup still applies Short-lived local processing when you do not need to choose the name
browser_type.launch(downloads_path=...) Selects the browser launch’s accepted-download directory Does not override deletion when the producing context closes Global storage configuration for a launch, not durable retention

save_as() may be called while the transfer is in progress; it waits as necessary. path() waits for completion and returns only after a successful download. See the BrowserType reference for launch configuration.

Waiting, timeouts, and failed downloads

Default timeout

page.expect_download() has a 30,000 millisecond default timeout. Set a larger value for a legitimately slow transfer, preferably for the specific wait:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
with page.expect_download(timeout=120_000) as download_info:
    page.get_by_role("button", name="Export").click()

You can also configure a context default timeout, but a local timeout documents which operation is expected to take longer. The Page API reference describes the event and timeout behavior.

Best Value
Sale
IMEASON Swivel Design 16GB USB Flash Drive with Keychain, USB 2.0 Portable Thumb Drive Memory Stick, FAT32 Format Flashdrive for Data Storage, Photos, Music, Files (Black, 16 GB)
  • 【16GB Flash Drive】USB flash drives with 16GB capacity, meet your needs of daily use on work, school, home and travelling for photos, music, videos, files storage and transfer. IMEASON thumb drives can be used to store different files, easy to data backup.
  • 【Metal Swivel Cap Design】USB thumb drive is metal swivel cover provides extra protection for the usb thumbdrive connector, no usb drive cap to lose; keychain design makes it easier to carry without worrying lose it.
  • 【Wide Compatibility】USB drive supports Windows 7/8/10/11 / Vista / XP / Unix / 2000 / ME / NT Linux and Mac OS, also Supports USB 2.0 and 1.1 ports. USB Stick support TV, desktop, notebook computer, car, audio and other device. The USB Memory Stick is your great data storage and transfer companion with traveling and working.
  • 【Easy to use】usb memory stick is plug and play without any software installation. Just simply plug the Flashdrive into the port of your USB-compatible devices such as computer, laptop to start data storage or transmission.
  • 【What You Get】16 GB USB Flash Drive Thumb Drive, The default format of the usb storage flash drive is FAT32.

Check the failure property

After receiving the object, inspect download.failure() (or await download.failure() asynchronously) when you need an explicit diagnostic. A failed transfer cannot be saved successfully; fix authentication, navigation, network, or server errors before processing the file.

When a click does not start a download

  • Confirm that the control really returns an attachment rather than opening a new tab, rendering a PDF, or navigating to an error page.
  • Register expect_download() immediately before the exact action, including any required preceding click that opens a menu.
  • Wait for authentication or application state before clicking; an expired session often produces HTML instead of the expected file.
  • Use a locator that targets the intended control, not a similarly named hidden element.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common errors and fixes

Symptom Likely cause Fix
“Timeout 30000ms exceeded” while waiting The action did not emit a download, or the transfer exceeded 30 seconds Verify the UI behavior and locator; then increase the expectation timeout for known slow files.
save_as reports a missing parent The destination directory was never created or the temporary scope already ended Create the directory first and save inside the active TemporaryDirectory block.
File disappears after the script It was Playwright-managed or inside a temporary directory that has been cleaned up Call save_as(), then copy or move the result to a persistent location before closing the context or temporary-directory block.
Unexpected filename or path traversal risk Using a server- or page-supplied suggested name directly Prefer a fixed name; otherwise reduce it to a basename and validate the extension and characters.
Async type errors Synchronous and asynchronous APIs were mixed Use one style throughout and await the async event result, actions, and save operation.
path() fails in a remote connection The API does not expose the managed path remotely Use save_as() to copy the file to a path accessible to the process that needs it.

Performance, concurrency, and reliability

  • Keep the browser alive for related downloads. Closing the context invalidates its managed files.
  • Use one temporary directory per job. This prevents concurrent workers from overwriting a common filename and makes cleanup atomic at the job boundary.
  • Do not poll the filesystem. The download event and save_as() provide synchronization and wait for transfer completion.
  • Limit retention deliberately. Temporary files can contain personal or confidential data; clean them promptly and set restrictive permissions when your operating system or deployment requires it.
  • Choose timeouts from observed transfer characteristics. A longer timeout handles slow servers; it does not repair a locator that never initiates a download.
  • Validate the result. Check that the destination exists and has the expected size or format before handing it to downstream code.

Install and use the Playwright Python library according to the Getting started - Library documentation, including the browser installation step required by your environment.

Or skip the browser setup

If your goal is a clean image or PDF of a web page rather than downloading an attachment through an interactive browser, ScreenshotNeo provides a single HTTP request. It accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, with the result identified by response headers. Its 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 per month without a card; paid plans start at $5 for 3,000.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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 documentation for options such as PNG, JPEG, WebP, PDF, full-page capture, custom waits, headers, cookies, and signed webhooks. Create a free ScreenshotNeo account to get the 1,000 monthly screenshots with no card.

FAQ

Does save_as() rename the original browser download?

No. It copies the download to the destination path; the browser-managed file remains subject to context cleanup.

Can I save before the download finishes?

Yes. save_as() waits for the transfer to finish when necessary.

Is downloads_path a permanent archive?

No. It selects launch storage, but context-owned downloads are still deleted when that context closes.

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.