Generate screenshots, PDFs, and scrape web content from any Python application — one requests call, no Playwright, no browser binary, no infrastructure.
Get Free API Keyimport requests, os
def screenshot(url, fmt='png', width=1280, full_page=False):
r = requests.get('https://api.snapapi.pics/v1/screenshot', params={
'access_key': os.environ['SNAPAPI_KEY'],
'url': url,
'format': fmt,
'width': width,
'full_page': str(full_page).lower(),
}, timeout=30)
r.raise_for_status()
return r.content
# Save to file
with open('screenshot.png', 'wb') as f:
f.write(screenshot('https://example.com'))
import httpx, os
async def screenshot_async(url: str, fmt: str = 'png') -> bytes:
params = {
'access_key': os.environ['SNAPAPI_KEY'],
'url': url,
'format': fmt,
'width': 1280,
'full_page': 'true',
}
async with httpx.AsyncClient(timeout=30) as client:
r = await client.get('https://api.snapapi.pics/v1/screenshot', params=params)
r.raise_for_status()
return r.content
from flask import Flask, request, send_file, abort
import requests, os, io
app = Flask(__name__)
@app.route('/screenshot')
def capture():
url = request.args.get('url')
if not url:
abort(400)
r = requests.get('https://api.snapapi.pics/v1/screenshot', params={
'access_key': os.environ['SNAPAPI_KEY'],
'url': url,
'format': 'png',
'width': 1280,
}, timeout=30)
return send_file(io.BytesIO(r.content), mimetype='image/png')
from fastapi import FastAPI, Query
from fastapi.responses import Response
import httpx, os
app = FastAPI()
@app.get('/screenshot')
async def capture(url: str = Query(...)):
params = {
'access_key': os.environ['SNAPAPI_KEY'],
'url': url,
'format': 'png',
'width': 1280,
}
async with httpx.AsyncClient(timeout=30) as client:
r = await client.get('https://api.snapapi.pics/v1/screenshot', params=params)
return Response(content=r.content, media_type='image/png')
def generate_pdf(url: str, paper: str = 'A4') -> bytes:
r = requests.get('https://api.snapapi.pics/v1/screenshot', params={
'access_key': os.environ['SNAPAPI_KEY'],
'url': url,
'format': 'pdf',
'pdf_format': paper,
'full_page': 'true',
'delay': 1000,
}, timeout=30)
r.raise_for_status()
return r.content
# Django view — stream PDF download
from django.http import HttpResponse
def invoice_pdf(request, invoice_id):
invoice = Invoice.objects.get(pk=invoice_id)
pdf_bytes = generate_pdf(request.build_absolute_uri(invoice.get_absolute_url()))
response = HttpResponse(pdf_bytes, content_type='application/pdf')
response['Content-Disposition'] = f'attachment; filename="invoice-{invoice_id}.pdf"'
return response
import requests, json, os
def extract(url: str, selector: str, fields: dict) -> list:
r = requests.get('https://api.snapapi.pics/v1/extract', params={
'access_key': os.environ['SNAPAPI_KEY'],
'url': url,
'selector': selector,
'fields': json.dumps(fields),
}, timeout=30)
r.raise_for_status()
return r.json()['data']
# Example: scrape Hacker News headlines
items = extract(
'https://news.ycombinator.com',
'.athing',
{'title': '.titleline a', 'points': '.score'}
)
for item in items:
print(item['title'], '-', item['points'])
from celery import shared_task
import requests, os
from myapp.models import Website
from myapp.storage import upload_image
@shared_task(bind=True, max_retries=3)
def capture_screenshot(self, website_id: int):
site = Website.objects.get(pk=website_id)
try:
r = requests.get('https://api.snapapi.pics/v1/screenshot', params={
'access_key': os.environ['SNAPAPI_KEY'],
'url': site.url,
'format': 'png',
'width': 1280,
}, timeout=30)
r.raise_for_status()
key = upload_image(r.content, f'screenshots/{website_id}.png')
site.screenshot_key = key
site.save()
except Exception as exc:
raise self.retry(exc=exc, countdown=60)
Free: 200 captures/month. Starter: $19/month (5K). Pro: $79/month (50K). No per-seat pricing. Sign up at snapapi.pics.
SnapAPI works with every major Python HTTP library and framework. Here's everything you need to use it in production Python applications.
import requests, os
def scrape(url: str, wait_for: str = None) -> dict:
params = {
'access_key': os.environ['SNAPAPI_KEY'],
'url': url,
}
if wait_for:
params['wait_for'] = wait_for
r = requests.get('https://api.snapapi.pics/v1/scrape', params=params, timeout=30)
r.raise_for_status()
return r.json() # Returns {'html': '...', 'text': '...', 'title': '...'}
# Get rendered HTML from a React SPA
result = scrape('https://example.com/products', wait_for='.product-list')
print(result['text']) # Clean text content
from django.views import View
from django.http import HttpResponse
import requests, os
class ReportPDFView(View):
def get(self, request, report_id):
report = Report.objects.get(pk=report_id, user=request.user)
report_url = request.build_absolute_uri(
reverse('report-detail', kwargs={'pk': report_id})
)
token = generate_temp_token(request.user) # short-lived auth bypass
r = requests.get('https://api.snapapi.pics/v1/screenshot', params={
'access_key': os.environ['SNAPAPI_KEY'],
'url': report_url + f'?token={token}',
'format': 'pdf',
'pdf_format': 'A4',
'full_page': 'true',
'delay': 1500,
}, timeout=30)
r.raise_for_status()
return HttpResponse(
r.content,
content_type='application/pdf',
headers={'Content-Disposition': f'attachment; filename="report-{report_id}.pdf"'}
)
import asyncio, httpx, os
from pathlib import Path
async def batch_capture(urls: list[str], output_dir: str = './screenshots'):
Path(output_dir).mkdir(exist_ok=True)
async with httpx.AsyncClient(timeout=30) as client:
tasks = [
client.get('https://api.snapapi.pics/v1/screenshot', params={
'access_key': os.environ['SNAPAPI_KEY'],
'url': url,
'format': 'png',
'width': 1280,
})
for url in urls
]
responses = await asyncio.gather(*tasks, return_exceptions=True)
results = []
for url, res in zip(urls, responses):
if isinstance(res, Exception):
results.append({'url': url, 'error': str(res)})
else:
filename = url.replace('https://', '').replace('/', '_') + '.png'
path = Path(output_dir) / filename
path.write_bytes(res.content)
results.append({'url': url, 'file': str(path)})
return results
# Usage
urls = ['https://example.com', 'https://another.com', 'https://third.com']
results = asyncio.run(batch_capture(urls))
for r in results:
print(r)
import boto3, requests, os
from datetime import datetime
s3 = boto3.client('s3')
def capture_and_store(url: str, bucket: str) -> str:
r = requests.get('https://api.snapapi.pics/v1/screenshot', params={
'access_key': os.environ['SNAPAPI_KEY'],
'url': url,
'format': 'webp', # Smaller file size for storage
'width': 1280,
'full_page': 'false',
}, timeout=30)
r.raise_for_status()
key = f"screenshots/{datetime.utcnow().strftime('%Y/%m/%d')}/{hash(url)}.webp"
s3.put_object(
Bucket=bucket,
Key=key,
Body=r.content,
ContentType='image/webp',
CacheControl='max-age=86400',
)
return f"https://{bucket}.s3.amazonaws.com/{key}"
Store your SNAPAPI_KEY in environment variables, never in source code. Use python-dotenv for local development. For production, use AWS Secrets Manager, GCP Secret Manager, or HashiCorp Vault. For Django apps, use django-environ. Set a request timeout of 30 seconds minimum to handle complex page renders. Implement exponential backoff for retry logic — SnapAPI returns 429 when you exceed your rate limit and 503 for transient server issues, both of which are safe to retry.
Use responses (for requests) or respx (for httpx) to mock SnapAPI calls in unit tests. Store a fixture PNG file in your test suite and return it as the mock response body. This keeps your tests fast and independent of external APIs while still exercising your screenshot handling code end-to-end.
Sign up free at snapapi.pics — 200 captures/month, instant API key, no credit card required. The Python SDK is available on PyPI as snapapi-python (coming soon) or use the raw requests approach shown above for zero-dependency integration.
What Python versions are supported? Any Python 3.8+ environment works. The requests library is the simplest choice for synchronous code. Use httpx with asyncio for async applications like FastAPI. Both work identically with SnapAPI's REST interface.
How do I handle rate limits in Python? Implement exponential backoff: catch the requests.exceptions.HTTPError with status 429, read the Retry-After response header, sleep for that duration, then retry. For high-volume batch jobs, add a semaphore to limit concurrent requests to a safe number (5-10) rather than firing all at once.
Can I use it with Pydantic for typed responses? Yes. The extract and scrape endpoints return JSON. Define Pydantic models for the response shape and parse with model.model_validate(r.json()). For screenshot and PDF endpoints, the response is binary so Pydantic is not needed.
How do I integrate with Django REST Framework? Create a DRF APIView or ViewSet that calls SnapAPI in the get or create method. Use DRF's FileResponse to stream the binary result back. For background capture, use DRF with Celery and return a task ID immediately, then poll for completion or deliver the result via webhook.
Does it work in AWS Lambda with Python? Yes. The requests library works in Lambda. No native binaries needed, so deployment is a standard zip file or Docker image. Set the Lambda timeout to at least 30 seconds to accommodate SnapAPI's maximum render time. Memory can stay at 128MB since you're making HTTP calls, not running a browser.
Sign up free at snapapi.pics and make your first screenshot call in under 5 minutes.
Python developers building production systems often need more than a simple HTTP call. SnapAPI's Python SDK supports async workflows, batch processing, and seamless integration with popular frameworks like Django, FastAPI, and Flask.
import asyncio
import aiohttp
async def capture_batch(urls: list[str], api_key: str):
async with aiohttp.ClientSession() as session:
tasks = []
for url in urls:
payload = {"url": url, "format": "png", "full_page": True}
tasks.append(
session.post(
"https://api.snapapi.pics/v1/screenshot",
json=payload,
headers={"X-Api-Key": api_key}
)
)
responses = await asyncio.gather(*tasks)
results = []
for resp in responses:
data = await resp.json()
results.append(data.get("url"))
return results
# Capture 10 URLs concurrently
urls = [f"https://example.com/page/{i}" for i in range(10)]
results = asyncio.run(capture_batch(urls, "sk_live_YOUR_KEY"))
print(f"Captured {len(results)} screenshots")
Production Python code should handle transient failures gracefully. SnapAPI returns standard HTTP status codes — 429 for rate limits, 503 for temporary unavailability — making it straightforward to implement exponential backoff with the tenacity library or a simple retry loop. Always cache screenshots where possible to reduce API calls and improve response times for your end users.
When integrating SnapAPI into Django or FastAPI applications, consider storing your API key in environment variables and using a centralized screenshot service class that your views can call. This keeps credentials out of your codebase, makes testing easier with mock implementations, and lets you swap providers without touching business logic. SnapAPI also supports webhook callbacks for long-running video captures so your Python web app never blocks waiting for a response.