Skip to main content

Official Python SDK for ScreenshotFreeAPI — screenshot-as-a-service

Project description

screenshotfreeapi Python SDK

Official Python SDK for ScreenshotFreeAPI — screenshot-as-a-service.

Capture pixel-perfect screenshots of any website, mobile app store listing, or raw HTML string with a single Python function call.

  • Zero dependencies — pure Python stdlib (urllib, json, hmac, hashlib)
  • Python 3.8+ compatible
  • Synchronous API with built-in retry and job polling
  • Full type annotations on all request/response objects

Installation

pip install screenshotfreeapi

Quick Start

from screenshotfreeapi import ScreenshotFreeAPIClient

client = ScreenshotFreeAPIClient(api_key="sfa_your_api_key_here")
result = client.capture("https://stripe.com")
print(result.screenshots[0].url)

Web Screenshots

Basic

result = client.capture("https://example.com")
print(result.screenshots[0].url)    # pre-signed S3 URL
print(result.screenshots[0].width)  # viewport width in pixels

AI-Targeted Sections

Describe the part of the page you want in plain English. ScreenshotFreeAPI uses Claude vision to find and crop exactly that element.

result = client.capture(
    "https://stripe.com/pricing",
    description="the pricing comparison table",
)
# metadata tells you what the AI did
print(result.metadata["aiConfidence"])   # e.g. 0.91
print(result.metadata["rawAiSelector"])  # e.g. ".pricing-table"

CSS Selector Targeting

result = client.capture(
    "https://github.com",
    element=".hero-section",
)

Full Page

result = client.capture("https://example.com", full_page=True)

PDF Generation

result = client.capture(
    "https://example.com",
    format="pdf",
    paper_size="A4",
)
print(result.screenshots[0].url)  # PDF download URL

All Options

result = client.capture(
    "https://stripe.com/pricing",
    description="the pricing table",   # AI targeting
    element=".pricing",                # CSS selector crop
    dimensions={"width": 1440, "height": 900},
    full_page=False,
    scrolling=False,
    format="png",                      # "png" | "jpeg" | "webp" | "pdf"
    paper_size="A4",                   # PDF only
    block_ads=True,
    accept_cookies=True,
    stealth=False,                     # BUSINESS plan
    proxy_location="us-east",          # BUSINESS plan
    bypass_cache=False,
    webhook_url="https://your-app.com/webhook",
    storage={                          # BUSINESS plan — bring your own S3
        "bucket": "my-bucket",
        "region": "us-east-1",
        "access_key_id": "AKIA...",
        "secret_access_key": "...",
    },
)
Option Type Default Description
url str required Page URL to capture
description str None Plain-English AI targeting description
element str None CSS selector to crop to
dimensions dict 1280×720 {"width": int, "height": int}
full_page bool False Capture full scrollable height
format str "png" "png", "jpeg", "webp", or "pdf"
paper_size str "A4" PDF paper size
block_ads bool False Strip ads (STARTER+)
accept_cookies bool False Dismiss cookie banners (STARTER+)
stealth bool False Stealth mode (BUSINESS+)
proxy_location str None Geo-proxy region (BUSINESS+)
bypass_cache bool False Skip cached result
webhook_url str None Webhook URL for async notification
storage dict None Custom S3 bucket (BUSINESS+)

Mobile Screenshots

Capture App Store and Google Play listing images, or use device emulation.

# App Store + Google Play listing images
result = client.screenshots.mobile_and_wait({
    "app_name": "Instagram",
    "platform": "both",               # "ios" | "android" | "both"
    "include_store_listing": True,
    "device_emulation": "iPhone 12",
})

for screenshot in result.screenshots:
    print(screenshot.url)
# By bundle ID
result = client.screenshots.mobile_and_wait({
    "bundle_id": "com.instagram.android",
    "platform": "android",
})

HTML Rendering

Convert raw HTML strings (email templates, reports, certificates) to screenshots.

html = """
<html>
  <body style="font-family:sans-serif; padding:40px">
    <h1>Invoice #1042</h1>
    <p>Total: <strong>$250.00</strong></p>
  </body>
</html>
"""

result = client.screenshots.html_and_wait({
    "html": html,
    "dimensions": {"width": 800, "height": 600},
    "format": "png",
})
print(result.screenshots[0].url)

Async Job Pattern

capture() and *_and_wait() are synchronous convenience wrappers that poll until the job finishes. For full control use the lower-level async pattern:

Manual enqueueing

# 1. Enqueue — returns immediately with a job ID
job = client.screenshots.web({"url": "https://example.com"})
print("Queued:", job.job_id)
print("Estimated:", job.estimated_seconds, "seconds")

# 2. Poll status manually
status = client.jobs.get_status(job.job_id)
print("Status:", status.status)   # "queued" | "processing" | "completed" | "failed"
print("Progress:", status.progress, "%")

# 3. Fetch result once completed
if status.status == "completed":
    result = client.jobs.get_result(job.job_id)
    print("URL:", result.screenshots[0].url)

Using wait() with a progress callback

def show_progress(pct: int):
    print(f"  {pct}% complete", end="\r")

job = client.screenshots.web({"url": "https://stripe.com"})
result = client.screenshots.wait(
    job.job_id,
    poll_interval_ms=2000,    # poll every 2 seconds
    timeout_ms=120_000,       # give up after 2 minutes
    on_progress=show_progress,
)
print("\nDone:", result.screenshots[0].url)

Webhooks (server push)

Instead of polling you can pass webhook_url and receive a POST when the job completes:

job = client.screenshots.web({
    "url": "https://example.com",
    "webhook_url": "https://your-server.com/webhooks/screenshot-events",
})
# Your server receives: POST /webhooks/screenshot-events { "jobId": "...", "status": "completed", ... }

Webhook Verification

Verify that incoming webhooks were sent by ScreenshotFreeAPI and have not been tampered with.

from screenshotfreeapi.webhooks import verify_webhook_signature

# Inside your webhook handler (framework-agnostic):
raw_body: str = request.body          # exact raw bytes decoded as UTF-8 string
signature: str = request.headers.get("X-ScreenshotFree-Signature", "")
secret: str = "your_webhook_secret"

if not verify_webhook_signature(raw_body, signature, secret):
    raise Exception("Invalid webhook signature — reject request")

event = json.loads(raw_body)
print(event["status"])  # "completed" | "failed"

Error Handling

All errors inherit from ScreenshotFreeAPIError. Catch the most specific class you care about:

from screenshotfreeapi import (
    ScreenshotFreeAPIClient,
    AuthenticationError,
    ForbiddenError,
    NotFoundError,
    ValidationError,
    RateLimitError,
    QuotaExceededError,
    PaymentRequiredError,
    JobFailedError,
    JobTimeoutError,
    ScreenshotFreeAPIError,
)

client = ScreenshotFreeAPIClient(api_key="sfa_...")

try:
    result = client.capture("https://example.com")

except AuthenticationError:
    # 401 — API key invalid or missing
    print("Check your API key")

except ForbiddenError:
    # 403 — key revoked or account suspended
    print("Account suspended — contact support")

except PaymentRequiredError:
    # 402 — subscription cancelled or payment failed
    print("Renew your subscription")

except ValidationError as e:
    # 400 — bad request parameters
    print("Request error:", e.details)

except RateLimitError as e:
    # 429 — too many requests per minute
    import time
    print(f"Rate limited, waiting {e.retry_after}s")
    time.sleep(e.retry_after)

except QuotaExceededError:
    # 429 with error=QuotaExceeded — monthly quota exhausted
    print("Monthly quota used up — upgrade your plan")

except NotFoundError:
    # 404 — job not found or not yours
    print("Job not found")

except JobFailedError as e:
    # Job reached FAILED status on the server
    print(f"Job {e.job_id} failed: {e.reason}")

except JobTimeoutError as e:
    # Polling timed out before job completed
    print(f"Job {e.job_id} timed out after {e.timeout_ms}ms")

except ScreenshotFreeAPIError as e:
    # Any other server-side error (5xx etc.)
    print(f"API error {e.status_code}: {e.message}")

Error class reference

Class HTTP When
ScreenshotFreeAPIError any Base class; also covers 5xx
AuthenticationError 401 Invalid or missing API key / JWT
ForbiddenError 403 Key revoked or account suspended
PaymentRequiredError 402 Subscription cancelled or payment overdue
NotFoundError 404 Resource not found or not owned by this key
ValidationError 400 Request body failed validation; details has field errors
RateLimitError 429 Per-minute rate limit hit; retry_after is seconds to wait
QuotaExceededError 429 Monthly screenshot quota exhausted
JobFailedError Job reached FAILED status; reason has the server message
JobTimeoutError Polling timed out; timeout_ms is the configured limit

Authentication

Register and get your API key

result = client.auth.register(
    email="you@example.com",
    password="strong-password-123!",
    name="Your Name",
)
print("API key (save this!):", result.api_key)

Obtain a management JWT (for billing/workspaces/monitors)

token_result = client.auth.get_token(
    email="you@example.com",
    password="strong-password-123!",
)
jwt = token_result.token
print("Token expires:", token_result.expires_at)

Refresh a JWT

new_token = client.auth.refresh_token(token_result.refresh_token)

Billing

Billing endpoints require a management JWT (see above).

# List all available plans (no auth)
plans = client.billing.list_plans()
for plan in plans:
    print(plan.name, plan.price_monthly, plan.screenshots_per_month)

# Current subscription
info = client.billing.get_current_plan(jwt)
print(info.plan, info.screenshots_used, "/", info.screenshots_limit)

# 30-day usage history
usage = client.billing.get_usage_history(jwt)
for day in usage:
    print(day.date, day.screenshots)

# Upgrade plan
upgrade = client.billing.upgrade_plan(jwt, plan="GROWTH")
print("Checkout URL:", upgrade.payment_link)

# Verify payment after redirect back
result = client.billing.verify_payment(jwt, tx_ref=upgrade.tx_ref)
print("Payment status:", result.status)

# Cancel subscription
client.billing.cancel(jwt)

Workspaces

Workspace endpoints require a management JWT.

# Create a workspace
ws = client.workspaces.create(jwt, name="Acme Screenshots")
print("Workspace ID:", ws.id)

# List workspaces
for ws in client.workspaces.list(jwt):
    print(ws.id, ws.name)

# Get workspace details
ws = client.workspaces.get(jwt, workspace_id="ws_abc")
for member in ws.members:
    print(member.email, member.role)

# Invite a member
client.workspaces.invite(jwt, ws.id, email="colleague@acme.com", role="member")

# Change member role
client.workspaces.update_member_role(jwt, ws.id, user_id="user_xyz", role="admin")

# Remove a member
client.workspaces.remove_member(jwt, ws.id, user_id="user_xyz")

App Monitors

Monitor endpoints require a management JWT.

# Create a monitor (checks Instagram daily at 9am)
monitor = client.monitors.create(jwt, {
    "app_name": "Instagram",
    "platform": "both",
    "schedule": "0 9 * * *",
    "webhook_url": "https://your-app.com/monitor-webhook",
    "notify_on_update": True,
})
print("Monitor ID:", monitor.id)

# List monitors
for m in client.monitors.list(jwt):
    print(m.id, m.app_name, m.status)

# Get monitor detail
m = client.monitors.get(jwt, monitor_id="mon_abc")

# Get run history
runs = client.monitors.get_history(jwt, monitor_id="mon_abc", limit=10)
for run in runs:
    print(run.run_at, run.changed, run.version_detected)

# Delete monitor
client.monitors.delete(jwt, monitor_id="mon_abc")

Zapier Integration

# Register a Zapier hook
sub = client.integrations.zapier_subscribe(
    event="screenshot.completed",
    target_url="https://hooks.zapier.com/hooks/catch/...",
)
print("Subscription ID:", sub.id)

# Unregister
client.integrations.zapier_unsubscribe(sub.id)

# Get a sample payload (for Zapier trigger setup)
samples = client.integrations.zapier_trigger_sample("screenshot.completed")
print(samples[0])

Configuration

Parameter Default Description
api_key required Your sfa_... API key
base_url https://api.screenshotfreeapi.com Override for self-hosted or testing
timeout 30 Socket timeout in seconds
max_retries 3 Retry attempts on 429 / 5xx before raising
client = ScreenshotFreeAPIClient(
    api_key="sfa_...",
    base_url="https://api.screenshotfreeapi.com",   # or "http://localhost:3000" for dev
    timeout=60,         # longer timeout for complex pages
    max_retries=5,      # extra retries for flaky connections
)

Health Check

health = client.health()
print(health.status)   # "ok" | "degraded" | "down"
print(health.version)  # API server version
print(health.uptime)   # seconds since last restart

Support


License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

screenshotfreeapi-1.0.0.tar.gz (35.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

screenshotfreeapi-1.0.0-py3-none-any.whl (31.2 kB view details)

Uploaded Python 3

File details

Details for the file screenshotfreeapi-1.0.0.tar.gz.

File metadata

  • Download URL: screenshotfreeapi-1.0.0.tar.gz
  • Upload date:
  • Size: 35.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for screenshotfreeapi-1.0.0.tar.gz
Algorithm Hash digest
SHA256 1e3e3cd6f5d24ad2d515c886050508a4597b686317e90c4050107727d28dadf6
MD5 1747ab9cbf18f89038b55832f17600cc
BLAKE2b-256 b95bcce26a6a1b4ad38d7ae6d1c814eda63fc27a9512395127f8e35ed1fb8c4b

See more details on using hashes here.

File details

Details for the file screenshotfreeapi-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for screenshotfreeapi-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0d2f89bf1f9bd4aa56e729b607d90f5f26e0ae3af4ba06a1d736addbadff1993
MD5 c1ed1e45e03130c42bdbc1915209893a
BLAKE2b-256 e3ea0f5c2dc1ffa604f95568e1e710b1997dd2c1858b8da61030aaaf3e2c2dc0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page