Screenshot -- Python SDK

Official Python SDK for the Nodium Screenshot API. Supports Python 3.9+ with both synchronous and asynchronous interfaces.

Installation

bash
pip install nodium-screenshot
bash
# or with poetry
poetry add nodium-screenshot

# with async support (installs httpx)
pip install nodium-screenshot[async]

Quick Start

python
from nodium_screenshot import NodiumScreenshot

client = NodiumScreenshot("YOUR_API_KEY")

result = client.take(
    url="https://example.com",
    format="png",
)

with open("screenshot.png", "wb") as f:
    f.write(result.data)

Client Initialization

python
from nodium_screenshot import NodiumScreenshot

# Basic initialization
client = NodiumScreenshot("YOUR_API_KEY")

# With options
client = NodiumScreenshot(
    api_key="YOUR_API_KEY",
    base_url="https://api.nodium.io/api/v1/screenshot",
    timeout=60,          # Request timeout in seconds (default: 60)
    retries=3,           # Automatic retries on 5xx errors (default: 3)
    retry_delay=1.0,     # Base delay between retries in seconds (default: 1.0)
)

Environment Variable

python
import os

os.environ["NODIUM_API_KEY"] = "YOUR_API_KEY"

# Reads from NODIUM_API_KEY automatically
client = NodiumScreenshot()

Synchronous Usage

take() -- Capture a Screenshot

python
result = client.take(
    url="https://example.com",
    format="png",
    viewport_width=1280,
    viewport_height=1024,
    full_page=True,
    image_quality=90,
)

# result.data     -- bytes with image/PDF content
# result.headers  -- dict with response headers
with open("screenshot.png", "wb") as f:
    f.write(result.data)

animate() -- Record an Animated Screenshot

python
result = client.animate(
    url="https://example.com",
    format="mp4",
    scroll_animation="smooth",
    animation_duration=5,
    viewport_width=1280,
    viewport_height=720,
)

with open("animation.mp4", "wb") as f:
    f.write(result.data)

bulk() -- Batch Capture

python
results = client.bulk(
    urls=[
        "https://example.com",
        "https://example.org",
        "https://example.net",
    ],
    format="png",
    viewport_width=1280,
    viewport_height=1024,
)

for i, result in enumerate(results):
    with open(f"screenshot-{i}.png", "wb") as f:
        f.write(result.data)

get_usage() -- Check API Usage

python
usage = client.get_usage()

print(f"Screenshots taken: {usage.screenshots_taken}")
print(f"Monthly limit: {usage.monthly_limit}")
print(f"Remaining: {usage.remaining}")
print(f"Billing period ends: {usage.period_end}")

Async Usage

The SDK provides an async client powered by httpx for non-blocking I/O.

python
from nodium_screenshot import AsyncNodiumScreenshot

client = AsyncNodiumScreenshot("YOUR_API_KEY")

async def capture():
    result = await client.take(
        url="https://example.com",
        format="png",
    )

    async with aiofiles.open("screenshot.png", "wb") as f:
        await f.write(result.data)

    await client.close()

Async Context Manager

python
async with AsyncNodiumScreenshot("YOUR_API_KEY") as client:
    result = await client.take(
        url="https://example.com",
        format="png",
    )

    async with aiofiles.open("screenshot.png", "wb") as f:
        await f.write(result.data)

Concurrent Captures

python
import asyncio

async def capture_multiple():
    async with AsyncNodiumScreenshot("YOUR_API_KEY") as client:
        urls = [
            "https://example.com",
            "https://example.org",
            "https://example.net",
        ]

        tasks = [
            client.take(url=url, format="png")
            for url in urls
        ]

        results = await asyncio.gather(*tasks)

        for i, result in enumerate(results):
            async with aiofiles.open(f"screenshot-{i}.png", "wb") as f:
                await f.write(result.data)

asyncio.run(capture_multiple())

Fluent Builder API

Build screenshot requests using a chainable interface.

python
result = (
    client.take()
    .url("https://example.com")
    .format("png")
    .viewport(1280, 1024)
    .full_page(True)
    .image_quality(90)
    .execute()
)

with open("screenshot.png", "wb") as f:
    f.write(result.data)

Builder Methods

MethodDescription
.url(url)Target URL to capture
.html(html)Raw HTML to render
.markdown(md)Markdown content to render
.format(fmt)Output format: png, jpeg, webp, avif, pdf, etc.
.viewport(width, height)Viewport dimensions
.viewport_device(device)Device emulation (e.g. iphone_16_pro)
.full_page(bool)Capture the entire scrollable page
.image_quality(n)JPEG/WebP quality (1-100)
.selector(css)Capture a specific CSS selector
.delay(ms)Wait before capture (milliseconds)
.block_ads(bool)Block ads and trackers
.dark_mode(bool)Force dark color scheme
.response_type(type)by_format, json, or empty
.webhook(url)Webhook URL for async delivery
.execute()Execute the request and return the result

Async Builder

The async client supports the same fluent API with await:

python
result = await (
    client.take()
    .url("https://example.com")
    .format("png")
    .viewport(1440, 900)
    .full_page(True)
    .execute()
)

Error Handling

The SDK raises typed exceptions for all API errors.

python
from nodium_screenshot import NodiumError, NodiumAPIError, NodiumTimeoutError

try:
    result = client.take(
        url="https://example.com",
        format="png",
    )

    with open("screenshot.png", "wb") as f:
        f.write(result.data)

except NodiumAPIError as e:
    print(f"Error code: {e.code}")            # e.g. "request_not_valid"
    print(f"Message: {e.message}")             # Human-readable description
    print(f"HTTP status: {e.http_status}")     # e.g. 400
    print(f"Retryable: {e.is_retryable}")      # True for 5xx errors

    if e.code == "screenshots_limit_reached":
        print("Monthly quota exceeded. Upgrade your plan.")

except NodiumTimeoutError:
    print("Request timed out. Try increasing the timeout.")

except NodiumError as e:
    # Base exception for all SDK errors
    print(f"SDK error: {e}")

Signed URL Generation

Generate pre-signed URLs for safe sharing without exposing your API key.

python
signed_url = client.generate_signed_url(
    url="https://example.com",
    format="png",
    viewport_width=1280,
    viewport_height=1024,
    expires_in=3600,  # Valid for 1 hour
)

print(signed_url)
# https://api.nodium.io/api/v1/screenshot/take?url=...&signature=...&expires=...

Embed in HTML

python
signed_url = client.generate_signed_url(
    url="https://example.com",
    format="png",
    expires_in=300,
)

html = f'<img src="{signed_url}" alt="Screenshot" />'

Django Integration

Settings

python
# settings.py
NODIUM_API_KEY = os.environ.get("NODIUM_API_KEY")

View Example

python
# views.py
from django.http import HttpResponse, JsonResponse
from django.conf import settings
from nodium_screenshot import NodiumScreenshot, NodiumAPIError

client = NodiumScreenshot(settings.NODIUM_API_KEY)

def capture_screenshot(request):
    url = request.GET.get("url")
    if not url:
        return JsonResponse({"error": "url parameter required"}, status=400)

    try:
        result = client.take(url=url, format="png", viewport_width=1280, viewport_height=1024)
        response = HttpResponse(result.data, content_type="image/png")
        response["Content-Disposition"] = 'inline; filename="screenshot.png"'
        return response

    except NodiumAPIError as e:
        return JsonResponse(
            {"error": e.code, "message": e.message},
            status=e.http_status,
        )

Flask Integration

python
from flask import Flask, request, Response, jsonify
from nodium_screenshot import NodiumScreenshot, NodiumAPIError

app = Flask(__name__)
client = NodiumScreenshot(os.environ["NODIUM_API_KEY"])

@app.route("/screenshot")
def screenshot():
    url = request.args.get("url")
    if not url:
        return jsonify(error="url parameter required"), 400

    try:
        result = client.take(url=url, format="png")
        return Response(result.data, mimetype="image/png")

    except NodiumAPIError as e:
        return jsonify(error=e.code, message=e.message), e.http_status

Full Example

A complete example that captures a screenshot, saves it to disk, and handles errors.

python
import os
import sys
from pathlib import Path
from nodium_screenshot import NodiumScreenshot, NodiumAPIError, NodiumError

def capture_and_save(target_url: str, output_path: str) -> None:
    client = NodiumScreenshot(os.environ.get("NODIUM_API_KEY"))

    try:
        # Check usage before capturing
        usage = client.get_usage()
        if usage.remaining <= 0:
            print("No screenshots remaining this billing period.", file=sys.stderr)
            sys.exit(1)

        print(f"Remaining quota: {usage.remaining}/{usage.monthly_limit}")

        # Take the screenshot using the fluent builder
        result = (
            client.take()
            .url(target_url)
            .format("png")
            .viewport(1440, 900)
            .full_page(True)
            .block_ads(True)
            .delay(1000)
            .image_quality(95)
            .execute()
        )

        # Ensure the output directory exists
        Path(output_path).parent.mkdir(parents=True, exist_ok=True)

        # Write the screenshot to disk
        with open(output_path, "wb") as f:
            f.write(result.data)

        print(f"Screenshot saved to {output_path}")
        print(f"Rendering time: {result.headers['rendering_seconds']}s")
        print(f"File size: {result.headers['size_bytes'] / 1024:.1f} KB")
        print(f"Reference: {result.headers['reference']}")

    except NodiumAPIError as e:
        print(f"API error [{e.code}]: {e.message}", file=sys.stderr)
        if e.is_retryable:
            print("This error is retryable. The SDK already retried automatically.", file=sys.stderr)
        sys.exit(1)

    except NodiumError as e:
        print(f"SDK error: {e}", file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    capture_and_save("https://example.com", "./output/screenshot.png")

Next Steps