Tutorial Mar 15, 2026

Screenshot API for Python Developers: Requests vs Hosted API

Take screenshots in Python with Selenium (heavy, slow, infrastructure) or an HTTP API (3 lines of code). Which approach is right for your use case?

You're building a Python app and need to take screenshots. Maybe you're:

You search "Python screenshot library" and find Selenium. It's been around for years. It's in PyPI. Thousands of projects use it.

Three days later, you're debugging WebDriver timeouts, wrestling with Firefox vs Chrome, and wondering why your screenshots look different on different machines.

There's a simpler way. Let me show you both approaches — Selenium and a hosted API — so you can decide which fits your project.

The Selenium Approach

Selenium is a browser automation framework. Here's the minimal example:

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get('https://example.com')
driver.save_screenshot('screenshot.png')
driver.quit()

That's 7 lines. Simple, right?

But in production, this becomes a nightmare. Here's what you'll actually need:

1. Browser Driver Management

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service

service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)

Now you need webdriver-manager as a dependency. It works, but adds complexity.

2. Headless Rendering & Options

from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--disable-gpu')
options.add_argument('--window-size=1280,720')
options.add_argument('--user-agent=Mozilla/5.0...')

driver = webdriver.Chrome(options=options)

Each flag is a gotcha. On Linux, you need --no-sandbox. On macOS, you don't. In Docker, you need --disable-dev-shm-usage. Get it wrong and your screenshots fail silently.

3. Waits and Timing

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver.get(url)

# Wait for page to load
WebDriverWait(driver, 10).until(
    EC.presence_of_all_elements_located((By.TAG_NAME, 'body'))
)

# Wait for JavaScript to finish (how long? nobody knows)
time.sleep(2)

# Now take screenshot
driver.save_screenshot('screenshot.png')

How long should you wait? 1 second? 2? 5? Too short and your screenshot shows a blank page. Too long and your service times out.

4. Error Handling & Cleanup

try:
    driver = webdriver.Chrome(options=options)
    driver.get(url)
    driver.save_screenshot(filename)
except TimeoutException:
    print('Page took too long to load')
except WebDriverException as e:
    print(f'Selenium error: {e}')
except Exception as e:
    print(f'Unknown error: {e}')
finally:
    driver.quit()

Now multiply this by every function that takes a screenshot. You're writing the same error handling 10+ times.

5. Infrastructure & Scaling

Selenium runs Chrome locally. Chrome takes 100–200MB of RAM per instance. If you have 10 concurrent requests, you need 1–2GB of RAM just for browsers.

In production:

Real-world cost at 10,000 screenshots/month:

And that's before you hit the limits of a single server.

The API Approach

Here's the same task with a hosted screenshot API:

import requests

response = requests.post(
    'https://pagebolt.dev/api/v1/screenshot',
    headers={'x-api-key': 'YOUR_API_KEY'},
    json={'url': 'https://example.com'}
)

with open('screenshot.png', 'wb') as f:
    f.write(response.content)

That's it. 8 lines, including the file write. No browser management. No memory leaks. No infrastructure.

Here's a more realistic production-ready example:

import os
import requests
import logging

logger = logging.getLogger(__name__)

def take_screenshot(url, filename, timeout=10):
    """Take a screenshot of a URL and save to file."""
    try:
        response = requests.post(
            'https://pagebolt.dev/api/v1/screenshot',
            headers={'x-api-key': os.environ['PAGEBOLT_API_KEY']},
            json={
                'url': url,
                'width': 1280,
                'height': 720,
                'blockAds': True,
                'blockBanners': True
            },
            timeout=timeout
        )

        if response.status_code != 200:
            logger.error(f'Screenshot failed: {response.status_code} {response.text}')
            raise Exception(f'API returned {response.status_code}')

        with open(filename, 'wb') as f:
            f.write(response.content)

        logger.info(f'Screenshot saved: {filename}')
        return filename

    except requests.Timeout:
        logger.error(f'Screenshot request timed out: {url}')
        raise
    except requests.RequestException as e:
        logger.error(f'Screenshot request failed: {e}')
        raise

That's 35 lines of real, production-ready code. Compare it to managing Selenium.

Feature Comparison

Feature Selenium Hosted API
Setup time1 hour5 minutes
Lines of code150+ (pools, error handling)30–50
InfrastructureYou manage Chrome, memory, scalingHandled for you
Cost (10k/month)$2,400/month$29/month
Device presetsManual setupBuilt-in (25+ presets)
PDF generationExtra library, extra complexityOne parameter
Reliable waitsGuessing (sleep)Built-in (networkidle)
Retry logicYou implement itIncluded
UptimeDepends on your infrastructure99.9% SLA
MonitoringYou do itIncluded

When to Use Selenium

Use Selenium if:

When to Use an API

Use an API if:

Real-World Example: Django Link Preview Service

You're building a service that generates preview cards for shared links (like Discord does).

With Selenium:

  1. Install Chrome in Docker
  2. Set up WebDriver
  3. Handle timeouts and retries
  4. Monitor memory usage
  5. Deploy to a server with enough RAM
  6. Scale horizontally as traffic grows
  7. Cost: $2,400+/month in infrastructure

With an API:

  1. pip install requests
  2. Call the API endpoint
  3. Save the image
  4. Return to user
  5. Cost: $29/month

The API approach takes one day. The Selenium approach takes two weeks.

Code Example: Flask Link Preview

from flask import Flask, request, jsonify
import requests
import os

app = Flask(__name__)

@app.route('/api/preview', methods=['POST'])
def create_preview():
    """Generate a link preview card."""
    data = request.get_json()
    url = data.get('url')

    if not url:
        return jsonify({'error': 'URL required'}), 400

    try:
        # Step 1: Take screenshot
        response = requests.post(
            'https://pagebolt.dev/api/v1/screenshot',
            headers={'x-api-key': os.environ['PAGEBOLT_API_KEY']},
            json={
                'url': url,
                'width': 1200,
                'height': 630,
                'blockAds': True,
                'blockBanners': True
            },
            timeout=10
        )

        if response.status_code != 200:
            return jsonify({'error': 'Screenshot failed'}), 500

        # Step 2: Inspect page metadata (optional)
        meta_response = requests.post(
            'https://pagebolt.dev/api/v1/inspect',
            headers={'x-api-key': os.environ['PAGEBOLT_API_KEY']},
            json={'url': url},
            timeout=10
        )

        metadata = meta_response.json() if meta_response.ok else {}

        # Step 3: Return preview card data
        import base64
        image_b64 = base64.b64encode(response.content).decode()

        return jsonify({
            'title': metadata.get('title', 'Untitled'),
            'description': metadata.get('description', ''),
            'imageBase64': image_b64,
            'url': url
        })

    except requests.Timeout:
        return jsonify({'error': 'Request timed out'}), 504
    except Exception as e:
        return jsonify({'error': str(e)}), 500

if __name__ == '__main__':
    app.run(debug=False)

That's everything. No Selenium. No browser management. No infrastructure.

Hybrid Approach

Some teams use both:

Selenium shines when you need to interact with the page. APIs shine when you just need static screenshots.

The Bottom Line

Selenium is powerful if you need it. But most teams don't. They need screenshots, and they need them to work without becoming infrastructure engineers.

An API costs $29/month and takes 5 minutes to integrate. Selenium costs $2,400+/month and two weeks to get right.

The choice depends on your use case. But for most Python projects, the API wins on simplicity, cost, and reliability.

Try PageBolt free — 100 requests/month, no credit card

3 lines of Python. No Chrome to manage. Works with requests, httpx, or any HTTP client.

Get free API key