Metadata-Version: 2.5
Name: renderscreenshot
Version: 1.0.2
Summary: Official Python SDK for RenderScreenshot - Screenshot API for developers
Project-URL: Homepage, https://renderscreenshot.com
Project-URL: Documentation, https://renderscreenshot.com/docs
Project-URL: Repository, https://github.com/Render-Screenshot/rs-python
Project-URL: Issues, https://github.com/Render-Screenshot/rs-python/issues
Project-URL: Changelog, https://github.com/Render-Screenshot/rs-python/blob/main/CHANGELOG.md
Author-email: Simon Chiu <simon@renderscreenshot.com>
License-Expression: MIT
License-File: LICENSE
Keywords: api,browser-rendering,og-image,pdf,renderscreenshot,screenshot,screenshot-api,social-cards,web-capture
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.25.0
Provides-Extra: dev
Requires-Dist: bandit>=1.7.0; extra == 'dev'
Requires-Dist: detect-secrets>=1.4.0; extra == 'dev'
Requires-Dist: mypy>=1.0; extra == 'dev'
Requires-Dist: pip-audit>=2.6.0; extra == 'dev'
Requires-Dist: pre-commit>=3.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest-httpx>=0.30.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# RenderScreenshot Python SDK

Official Python SDK for [RenderScreenshot](https://renderscreenshot.com) - Screenshot API for developers.

[![CI](https://github.com/Render-Screenshot/rs-python/actions/workflows/ci.yml/badge.svg)](https://github.com/Render-Screenshot/rs-python/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Installation

```bash
pip install git+https://github.com/Render-Screenshot/rs-python.git
```

The package is not on PyPI yet. Until it is, install it from GitHub.

## Quick Start

```python
from renderscreenshot import Client, TakeOptions

# Initialize client with your live API key
client = Client("rs_live_xxxxx")

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

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

## Features

- **Simple API** - Fluent builder pattern for easy configuration
- **Full Type Hints** - Complete type annotations for IDE support
- **Presets** - Built-in presets for common use cases (OG cards, Twitter cards, etc.)
- **Device Emulation** - Emulate iPhone, Pixel, iPad, and more
- **Content Blocking** - Block ads, trackers, cookie banners, and chat widgets
- **PDF Generation** - Convert pages to PDF with full customization
- **Batch Processing** - Process multiple URLs efficiently
- **Cache Management** - Control caching and purge cached screenshots
- **Webhook Support** - Verify and parse webhook payloads
- **Signed URLs** - Generate signed URLs for embedding

## Usage

### Basic Screenshot

```python
from renderscreenshot import Client, TakeOptions

client = Client("rs_live_xxxxx")

# Simple screenshot
image = client.take(TakeOptions.url("https://example.com"))

# With options
options = (
    TakeOptions.url("https://example.com")
    .width(1200)
    .height(630)
    .format("png")
)
image = client.take(options)
```

### Using Presets

```python
# OG Card (1200x630)
options = TakeOptions.url("https://example.com").preset("og_card")

# Twitter Card
options = TakeOptions.url("https://example.com").preset("twitter_card")

# Full Page Screenshot
options = TakeOptions.url("https://example.com").preset("full_page")
```

### Device Emulation

```python
# iPhone 14 Pro
options = TakeOptions.url("https://example.com").device("iphone_14_pro")

# Pixel 7
options = TakeOptions.url("https://example.com").device("pixel_7")

# iPad Pro
options = TakeOptions.url("https://example.com").device("ipad_pro_12")
```

### Content Blocking

```python
options = (
    TakeOptions.url("https://example.com")
    .block_ads()
    .block_trackers()
    .block_cookie_banners()
    .block_chat_widgets()
)
```

### Dark Mode

```python
options = (
    TakeOptions.url("https://example.com")
    .dark_mode()
    .preset("og_card")
)
```

### PDF Generation

```python
options = (
    TakeOptions.url("https://example.com")
    .format("pdf")
    .pdf_paper_size("a4")
    .pdf_margin("1in")
    .pdf_print_background()
)
pdf = client.take(options)
```

### JSON Response

```python
# Get metadata along with screenshot URL
response = client.take_json(
    TakeOptions.url("https://example.com").preset("og_card")
)
print(f"Request ID: {response['id']}")  # req_...
image = response["image"]
print(f"Screenshot URL: {image['url']}")
print(f"Size: {image['width']}x{image['height']}")
print(f"File size: {image['size']} bytes")

# Optional sections
cache = response.get("cache", {})
print(f"Cache hit: {cache.get('hit')}, key: {cache.get('key')}")
usage = response.get("usage", {})
print(f"Credits used: {usage.get('credits')}, remaining: {usage.get('remaining')}")
```

The response has `id`, `status`, `image` (`url`, `width`, `height`, `size`,
`format`), and optional `metadata`, `cache` (`hit`, `key`, `url`, `expires_at`)
and `usage` (`credits`, `remaining`) objects.

### Batch Processing

```python
# Multiple URLs with shared options
urls = ["https://example1.com", "https://example2.com", "https://example3.com"]
options = TakeOptions.url("").preset("og_card")
results = client.batch(urls, options)

# Per-URL options
requests = [
    {"url": "https://example1.com", "options": {"width": 1200}},
    {"url": "https://example2.com", "options": {"preset": "full_page"}},
]
results = client.batch(requests)

# Check batch status
status = client.get_batch("batch_123")
print(f"Completed: {status['completed']}/{status['total']}")
```

### Signed URLs

Signed URLs call the `GET /v1/screenshot` render endpoint without an API key,
so you can put them in `<img>` tags or `og:image` meta tags. Signing needs your
public key ID (`rs_pub_...`) and your secret signing key (`rs_secret_...`) from
the dashboard. Your live API key is not used for signing. Keep the secret key on
your server.

```python
from datetime import datetime, timedelta

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

# Generate a signed URL that expires in 24 hours (max 30 days)
options = TakeOptions.url("https://example.com").preset("og_card")
signed_url = client.generate_url(
    options,
    datetime.now() + timedelta(hours=24),
)

# Or pass the credentials per call
signed_url = client.generate_url(
    options,
    datetime.now() + timedelta(hours=24),
    signing_key="rs_secret_xxxxx",
    public_key_id="rs_pub_xxxxx",
)

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

`generate_url()` raises `RenderScreenshotError` (code `invalid_request`) when
`signing_key` or `public_key_id` is missing. A signed URL only carries the
options that the GET endpoint reads (target, viewport size and scale, preset,
device, `full_page`, `element`, format, quality, delay, timeout, click, hide,
remove, ad and cookie-banner blocking, dark mode, user agent, locale, timezone,
cache and PDF options). Use `client.take()` for all other options.

### Cache Management

```python
# Get cached screenshot
image = client.cache.get("cache_key")

# Delete cache entry
deleted = client.cache.delete("cache_key")

# Bulk purge
result = client.cache.purge(["key1", "key2", "key3"])

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

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

### Webhook Verification

```python
from renderscreenshot import verify_webhook, parse_webhook

# In your webhook handler
def handle_webhook(request):
    signature = request.headers.get("X-Webhook-Signature")
    timestamp = request.headers.get("X-Webhook-Timestamp")
    payload = request.get_data(as_text=True)

    if not verify_webhook(payload, signature, timestamp, WEBHOOK_SECRET):
        return "Invalid signature", 401

    event = parse_webhook(payload)

    if event["type"] == "screenshot.completed":
        response = event["data"]["response"]
        print(f"Screenshot ready: {response['url']}")
    elif event["type"] == "screenshot.failed":
        error = event["data"]["error"]
        print(f"Failed ({error['code']}): {error['message']}")
    elif event["type"] in ("batch.completed", "batch.failed"):
        print(f"Batch {event['data']['batch_id']}: {event['data'].get('summary')}")

    return "OK", 200
```

### Request Format

`take()`, `take_json()` and `batch()` send the nested JSON body that the API
expects (for example `{"viewport": {"width": 1200}, "output": {"format": "png"}}`).
`TakeOptions.to_params()` returns this body. `TakeOptions.to_query_string()` and
`TakeOptions.to_flat_params()` return the flat query parameters for
`GET /v1/screenshot`. `take_json()` sends `Accept: application/json`.

### Context Manager

```python
# Automatically closes the client when done
with Client("rs_live_xxxxx") as client:
    image = client.take(TakeOptions.url("https://example.com"))
```

## All Options

### Target
- `url(str)` - URL to capture
- `html(str)` - HTML content to render

### Viewport
- `width(int)` - Viewport width in pixels
- `height(int)` - Viewport height in pixels
- `scale(float)` - Device scale factor (1-3)
- `mobile(bool)` - Enable mobile emulation

### Capture
- `full_page(bool)` - Capture full scrollable page
- `element(str)` - CSS selector for element capture
- `format(str)` - Output format: `png`, `jpeg`, `webp`, `pdf`
- `quality(int)` - JPEG/WebP quality (1-100)

### Wait
- `wait_for(str)` - Wait condition: `load`, `networkidle`, `domcontentloaded`
- `delay(int)` - Additional delay in milliseconds
- `wait_for_selector(str)` - Wait for CSS selector to appear
- `wait_for_timeout(int)` - Maximum wait time in seconds (1-60)

### Presets
- `preset(str)` - Use preset: `og_card`, `twitter_card`, `full_page`, etc.
- `device(str)` - Emulate device: `iphone_14_pro`, `pixel_7`, etc.

### Blocking
- `block_ads(bool)` - Block ad network domains
- `block_trackers(bool)` - Block analytics/tracking
- `block_cookie_banners(bool)` - Auto-dismiss cookie popups
- `block_chat_widgets(bool)` - Block chat widgets
- `block_urls(list)` - Block URLs matching patterns
- `block_resources(list)` - Block resource types: `font`, `media`, `image`, `script`, `stylesheet`

### Page Manipulation
- `inject_script(str)` - Inject JavaScript
- `inject_style(str)` - Inject CSS
- `click(str)` - Click element before capture
- `hide(list)` - Hide elements (visibility: hidden)
- `remove(list)` - Remove elements from DOM

### Browser
- `dark_mode(bool)` - Enable dark mode
- `reduced_motion(bool)` - Enable reduced motion
- `media_type(str)` - Media type: `screen`, `print`
- `user_agent(str)` - Custom user agent
- `timezone(str)` - IANA timezone
- `locale(str)` - BCP 47 locale
- `geolocation(lat, lng, accuracy)` - Spoof geolocation

### Network
- `headers(dict)` - Custom HTTP headers
- `cookies(list)` - Cookies to set
- `auth_basic(username, password)` - HTTP Basic auth
- `auth_bearer(token)` - Bearer token auth
- `bypass_csp(bool)` - Bypass Content Security Policy

### Cache
- `cache_ttl(int)` - Cache TTL in seconds
- `cache_refresh(bool)` - Force cache refresh
- `cache_available(bool)` - Reuse an identical cached render inside the TTL (0 credits). Without it every request renders a fresh screenshot

### PDF (when format is "pdf")
- `pdf_paper_size(str)` - Paper size: `a4`, `letter`, etc.
- `pdf_width(str)` - Custom width (CSS units)
- `pdf_height(str)` - Custom height
- `pdf_landscape(bool)` - Landscape orientation
- `pdf_margin(str)` - Uniform margin
- `pdf_margin_top/right/bottom/left(str)` - Individual margins
- `pdf_scale(float)` - Scale factor (0.1-2.0)
- `pdf_print_background(bool)` - Include backgrounds
- `pdf_page_ranges(str)` - Page ranges
- `pdf_header(str)` - Header HTML
- `pdf_footer(str)` - Footer HTML
- `pdf_fit_one_page(bool)` - Fit to single page
- `pdf_prefer_css_page_size(bool)` - Use CSS page size

### Storage (BYOS)
- `storage_enabled(bool)` - Enable custom storage
- `storage_path(str)` - Path template
- `storage_acl(str)` - ACL: `public-read`, `private`

## Error Handling

```python
from renderscreenshot import Client, TakeOptions, RenderScreenshotError

client = Client("rs_live_xxxxx")

try:
    image = client.take(TakeOptions.url("https://example.com"))
except RenderScreenshotError as e:
    print(f"Error: {e.message}")
    print(f"Code: {e.code}")
    print(f"HTTP Status: {e.http_status}")
    print(f"Param: {e.param}")  # e.g. "url" for validation errors
    print(f"Request ID: {e.request_id}")

    if e.retryable:
        if e.retry_after:
            print(f"Retry after {e.retry_after} seconds")
        # Implement retry logic
```

## Requirements

- Python 3.9+
- httpx

## License

MIT License - see [LICENSE](LICENSE) for details.

## Links

- [Documentation](https://renderscreenshot.com/docs)
- [API Reference](https://renderscreenshot.com/docs/api)
- [GitHub](https://github.com/Render-Screenshot/rs-python)
