Python SDK

The official Python SDK for RenderScreenshot. For asyncio, FastAPI, Django and pytest examples, see the Python screenshot API guide.

Installation

pip install renderscreenshot

The package is on PyPI.

Requirements

  • Python 3.9 or higher

Quick Start

from renderscreenshot import Client, TakeOptions

client = Client('rs_live_xxxxx')

# Take a screenshot
image = client.take(
    TakeOptions.url('https://example.com').preset('og_card')
)

# Save to file
with open('screenshot.png', 'wb') as f:
    f.write(image)

Client

Constructor

from renderscreenshot import Client

client = Client(
    api_key,
    base_url=None,
    timeout=None,
    signing_key=None,
    public_key_id=None,
)
Parameter Type Description
api_key str Your live API key (rs_live_*)
base_url str API base URL (default: https://api.renderscreenshot.com)
timeout float Request timeout in seconds (default: 30.0)
signing_key str Secret signing key (rs_secret_*). Only needed for generate_url()
public_key_id str Public key ID (rs_pub_*). Only needed for generate_url()

Context Manager

The client can be used as a context manager to automatically close connections:

with Client('rs_live_xxxxx') as client:
    image = client.take(options)

Methods

take(options)

Take a screenshot and return binary data. The SDK sends a POST /v1/screenshot request with a nested JSON body (for example {"viewport": {"width": 1200}, "output": {"format": "png"}}).

image = client.take(
    TakeOptions.url('https://example.com').preset('og_card')
)

# Save to file
with open('screenshot.png', 'wb') as f:
    f.write(image)

Returns: bytes

take_json(options)

Take a screenshot and return JSON metadata. The SDK sends the Accept: application/json header, so the API returns JSON instead of image bytes.

response = client.take_json(
    TakeOptions.url('https://example.com').preset('og_card')
)

print(response['id'])                 # req_...
print(response['image']['url'])       # CDN URL
print(response['image']['width'])     # 1200
print(response['image']['height'])    # 630
print(response['cache']['hit'])       # True/False
print(response['usage']['remaining']) # Credits left

Returns: ScreenshotResponse (TypedDict) with these keys:

Key Description
id Request ID (req_...)
status completed (or processing for a request with a webhook, which returns HTTP 202 with a job_... ID)
image url, width, height, size, format
metadata Optional. title
cache Optional. hit, key, url, expires_at
usage Optional. credits, remaining

generate_url(options, expires_at, *, signing_key=None, public_key_id=None)

Generate a signed URL for public embedding. A signed URL calls the GET /v1/screenshot render endpoint without an API key. Signing needs your public key ID (rs_pub_*) and your secret signing key (rs_secret_*). Your live API key is not used for signing. Keep the secret key on your server.

from datetime import datetime, timedelta

client = Client(
    'rs_live_xxxxx',
    signing_key='rs_secret_xxxxx',
    public_key_id='rs_pub_xxxxx',
)

signed_url = client.generate_url(
    TakeOptions.url('https://example.com').preset('og_card'),
    datetime.now() + timedelta(hours=24),  # max 30 days
)

# Use in HTML: <img src="{signed_url}" />

# Or pass the signing credentials per call
signed_url = client.generate_url(
    TakeOptions.url('https://example.com').preset('og_card'),
    datetime.now() + timedelta(hours=24),
    signing_key='rs_secret_xxxxx',
    public_key_id='rs_pub_xxxxx',
)

expires_at is a datetime or a Unix timestamp in seconds. The URL contains key_id, expires, the screenshot options as flat query parameters, and a signature. If signing_key or public_key_id is missing, generate_url() raises RenderScreenshotError with the code invalid_request.

A signed URL only carries the options that the GET endpoint reads: url, html, preset, device, width, height, scale, full_page, element, format, quality, delay, wait_for_timeout, click, hide, remove, block_ads, block_cookie_banners, dark_mode, user_agent, locale, timezone, cache_ttl, cache_refresh, and the pdf_* options. Use take() for all other options.

Returns: str

batch(urls, options) / batch(requests)

Process multiple screenshots in a batch.

# Simple batch with shared options
results = client.batch(
    ['https://example1.com', 'https://example2.com'],
    TakeOptions.url('').preset('og_card'),
)

# Advanced batch with per-URL options
results = client.batch([
    {'url': 'https://example1.com', 'options': {'preset': 'og_card'}},
    {'url': 'https://example2.com', 'options': {'width': 1920}},
])

Returns: BatchResponse (TypedDict)

get_batch(batch_id)

Get the status of a batch job.

status = client.get_batch('batch_abc123')
print(status['completed'], status['total'])

Returns: BatchResponse (TypedDict)

presets() / preset(id)

Get available presets.

presets = client.presets()
og_card = client.preset('og_card')

devices()

Get available device presets.

devices = client.devices()

TakeOptions

Fluent builder for screenshot options.

Creating Options

from renderscreenshot import TakeOptions

# From URL
options = TakeOptions.url('https://example.com')

# From HTML
options = TakeOptions.html('<h1>Hello World</h1>')

# From config dict
options = TakeOptions.from_config({'url': '...', 'width': 1200})

Viewport Options

options = (
    TakeOptions.url('...')
    .width(1200)       # Viewport width
    .height(630)       # Viewport height
    .scale(2)          # Device scale factor (1-3)
    .mobile()          # Enable mobile emulation
)

Capture Options

options = (
    TakeOptions.url('...')
    .full_page()       # Capture full scrollable page
    .element('.hero')  # Capture specific element
    .format('png')     # png, jpeg, webp, pdf
    .quality(90)       # JPEG/WebP quality (1-100)
)

Wait Options

options = (
    TakeOptions.url('...')
    .wait_for('networkidle')      # load, networkidle, domcontentloaded
    .delay(2000)                  # Additional delay (ms)
    .wait_for_selector('.loaded') # Wait for selector
    .wait_for_timeout(30)         # Max wait time (s, 1-60)
)

Presets & Devices

options = (
    TakeOptions.url('...')
    .preset('og_card')           # Use a preset
    .device('iphone_14_pro')     # Emulate a device
)

Content Blocking

options = (
    TakeOptions.url('...')
    .block_ads()                  # Block ad networks
    .block_trackers()             # Block analytics
    .block_cookie_banners()       # Auto-dismiss cookie popups
    .block_chat_widgets()         # Block chat widgets
    .block_urls(['*.ads.com/*'])  # Block URL patterns
    .block_resources(['font'])    # Block resource types
)

Browser Emulation

options = (
    TakeOptions.url('...')
    .dark_mode()                  # Enable dark mode
    .reduced_motion()             # Prefer reduced motion
    .media_type('print')          # screen or print
    .user_agent('Custom UA')      # Custom user agent
    .timezone('America/New_York')
    .locale('en-US')
    .geolocation(40.7128, -74.006)
)

Network Options

options = (
    TakeOptions.url('...')
    .headers({'X-Custom': 'value'})
    .cookies([{'name': 'session', 'value': 'abc', 'domain': 'example.com'}])
    .auth_basic('user', 'pass')   # or .auth_bearer('token'); basic wins if both are set
    .bypass_csp()
)

Cache Options

options = (
    TakeOptions.url('...')
    .cache_ttl(86400)             # Cache TTL in seconds
    .cache_available()            # Reuse an identical render inside the TTL (0 credits)
    .cache_refresh()              # Force refresh
)

PDF Options

options = (
    TakeOptions.url('...')
    .format('pdf')
    .pdf_paper_size('a4')          # a4, letter, legal, etc.
    .pdf_landscape()
    .pdf_margin('1in')
    .pdf_print_background()
    .pdf_header('<div>Header</div>')
    .pdf_footer('<div>Page <span class="pageNumber"></span></div>')
)

Storage Options (BYOS)

options = (
    TakeOptions.url('...')
    .storage_enabled()
    .storage_path('{year}/{month}/{hash}.{ext}')
    .storage_acl('public-read')
)

Cache Management

# Get cached screenshot
image = client.cache.get('cache_xyz789')

# Delete single entry
client.cache.delete('cache_xyz789')

# Bulk purge by keys
client.cache.purge(['cache_abc', 'cache_def'])

# Purge by URL pattern
client.cache.purge_url('https://mysite.com/blog/*')

# Purge by date
from datetime import datetime
client.cache.purge_before(datetime(2024, 1, 1))

Webhook Helpers

from renderscreenshot import verify_webhook, parse_webhook

# Verify signature
is_valid = verify_webhook(payload, signature, timestamp, secret)

# Parse event
event = parse_webhook(payload)  # reads the payload's `event` field into event['type']
if event['type'] == 'screenshot.completed':
    print(event['data']['response']['url'])
elif event['type'] == 'screenshot.failed':
    print(event['data']['error']['code'])     # from data.error_code, e.g. 'timeout'
    print(event['data']['error']['message'])
elif event['type'] in ('batch.completed', 'batch.failed'):
    print(event['data']['batch_id'], event['data'].get('summary'))

Error Handling

from renderscreenshot import Client, TakeOptions, RenderScreenshotError

try:
    client.take(TakeOptions.url('...'))
except RenderScreenshotError as e:
    print(e.http_status)   # 400, 429, etc.
    print(e.code)          # 'invalid_url', 'rate_limited'
    print(e.message)       # Human-readable message
    print(e.retryable)     # True/False
    print(e.retry_after)   # Seconds (for rate limits)

Error Codes

Code HTTP Retryable Description
invalid_request 400 No Malformed request
invalid_url 400 No Invalid URL provided
unauthorized 401 No Invalid API key
forbidden 403 No Access denied
not_found 404 No Resource not found
rate_limited 429 Yes Rate limit exceeded
timeout 408 Yes Screenshot timed out
render_failed 500 Yes Browser rendering failed
internal_error 500 Yes Internal server error

Type Hints

The SDK includes full type hints via py.typed marker and TypedDict definitions:

from renderscreenshot import (
    Client,
    TakeOptions,
    RenderScreenshotError,
    ScreenshotResponse,
    BatchResponse,
    TakeOptionsConfig,
)

Was this page helpful?