snaprender 0.7.0__tar.gz → 0.7.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- snaprender-0.7.1/PKG-INFO +451 -0
- snaprender-0.7.1/README.md +437 -0
- {snaprender-0.7.0 → snaprender-0.7.1}/pyproject.toml +1 -1
- snaprender-0.7.0/PKG-INFO +0 -166
- snaprender-0.7.0/README.md +0 -152
- {snaprender-0.7.0 → snaprender-0.7.1}/.gitignore +0 -0
- {snaprender-0.7.0 → snaprender-0.7.1}/LICENSE +0 -0
- {snaprender-0.7.0 → snaprender-0.7.1}/snaprender/__init__.py +0 -0
- {snaprender-0.7.0 → snaprender-0.7.1}/snaprender/client.py +0 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: snaprender
|
|
3
|
+
Version: 0.7.1
|
|
4
|
+
Summary: Official Python SDK for SnapRender Screenshot API
|
|
5
|
+
Project-URL: Homepage, https://snap-render.com
|
|
6
|
+
Project-URL: Documentation, https://snap-render.com/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/User0856/snaprender
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: api,capture,screenshot,snaprender,webpage
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Requires-Dist: httpx>=0.24
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# snaprender
|
|
16
|
+
|
|
17
|
+
[](https://pypi.org/project/snaprender/)
|
|
18
|
+
[](https://pypi.org/project/snaprender/)
|
|
19
|
+
[](https://github.com/User0856/snaprender/blob/main/LICENSE)
|
|
20
|
+
|
|
21
|
+
Official Python SDK for the [SnapRender](https://snap-render.com) Screenshot API. Capture pixel-perfect screenshots, extract page content, run batch jobs, and manage webhooks with a single library.
|
|
22
|
+
|
|
23
|
+
**500 free screenshots/month. [Get your API key](https://snap-render.com/auth/signup?ref=pypi)**
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- **Multiple output formats** : PNG, JPEG, WebP, and PDF
|
|
28
|
+
- **Three input modes** : URL, raw HTML, or Markdown
|
|
29
|
+
- **Signed URLs** : generate pre-signed screenshot URLs (no API key needed at render time)
|
|
30
|
+
- **Content extraction** : pull Markdown, plain text, HTML, article, links, or metadata from any page
|
|
31
|
+
- **Batch screenshots** : capture up to 50 URLs in a single job
|
|
32
|
+
- **Webhooks** : receive real-time notifications for screenshot and quota events
|
|
33
|
+
- **Device emulation** : iPhone, iPad, Pixel, and more presets
|
|
34
|
+
- **Dark mode** : capture pages with `prefers-color-scheme: dark`
|
|
35
|
+
- **Ad blocking** : remove ads automatically before capture
|
|
36
|
+
- **Cookie banner removal** : clean screenshots without consent popups
|
|
37
|
+
- **Smart caching** : configurable CDN cache with TTL control
|
|
38
|
+
- **Full-page captures** : screenshot the entire scrollable page
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install snaprender
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from snaprender import SnapRender
|
|
50
|
+
|
|
51
|
+
snap = SnapRender(api_key="sk_live_...")
|
|
52
|
+
|
|
53
|
+
# Capture by URL
|
|
54
|
+
image = snap.capture("https://example.com")
|
|
55
|
+
with open("screenshot.png", "wb") as f:
|
|
56
|
+
f.write(image)
|
|
57
|
+
|
|
58
|
+
# Capture from raw HTML
|
|
59
|
+
image = snap.capture(html="<h1>Hello World</h1>")
|
|
60
|
+
|
|
61
|
+
# Capture from Markdown
|
|
62
|
+
image = snap.capture(markdown="# Hello World\nSome **bold** text.")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Context Manager
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
with SnapRender(api_key="sk_live_...") as snap:
|
|
69
|
+
image = snap.capture("https://example.com")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The HTTP connection is closed automatically when the block exits.
|
|
73
|
+
|
|
74
|
+
## API Reference
|
|
75
|
+
|
|
76
|
+
### Constructor
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
SnapRender(api_key, base_url="https://app.snap-render.com", timeout=60.0)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
| Parameter | Type | Default | Description |
|
|
83
|
+
|-----------|------|---------|-------------|
|
|
84
|
+
| `api_key` | str | (required) | Your SnapRender API key (`sk_live_...`) |
|
|
85
|
+
| `base_url` | str | `"https://app.snap-render.com"` | API base URL |
|
|
86
|
+
| `timeout` | float | `60.0` | Request timeout in seconds |
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### `snap.capture(url, *, html, markdown, **options)` -> `bytes | dict`
|
|
91
|
+
|
|
92
|
+
Take a screenshot. Provide exactly **one** of `url`, `html`, or `markdown`.
|
|
93
|
+
|
|
94
|
+
- When `url` is provided alone, the request is sent as GET with query parameters.
|
|
95
|
+
- When `html` or `markdown` is provided, the request is sent as POST with a JSON body.
|
|
96
|
+
- Returns raw image bytes by default. Set `response_type="json"` to get a dict with metadata and a base64 data URI.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
# URL with options
|
|
100
|
+
jpg = snap.capture(
|
|
101
|
+
"https://example.com",
|
|
102
|
+
format="jpeg",
|
|
103
|
+
width=1920,
|
|
104
|
+
height=1080,
|
|
105
|
+
full_page=True,
|
|
106
|
+
dark_mode=True,
|
|
107
|
+
quality=95,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# HTML input
|
|
111
|
+
png = snap.capture(html="<h1 style='color:red'>Red Title</h1>", width=800)
|
|
112
|
+
|
|
113
|
+
# Markdown input
|
|
114
|
+
png = snap.capture(markdown="# Report\n\n| Col A | Col B |\n|-------|-------|\n| 1 | 2 |")
|
|
115
|
+
|
|
116
|
+
# JSON response (base64 data URI + metadata)
|
|
117
|
+
result = snap.capture("https://example.com", response_type="json")
|
|
118
|
+
print(result["image"]) # data:image/png;base64,...
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Options**
|
|
122
|
+
|
|
123
|
+
| Option | Type | Default | Description |
|
|
124
|
+
|--------|------|---------|-------------|
|
|
125
|
+
| `url` | str | `None` | URL to capture |
|
|
126
|
+
| `html` | str | `None` | Raw HTML to render |
|
|
127
|
+
| `markdown` | str | `None` | Markdown to render |
|
|
128
|
+
| `format` | str | `"png"` | `"png"`, `"jpeg"`, `"webp"`, or `"pdf"` |
|
|
129
|
+
| `width` | int | `1280` | Viewport width in pixels |
|
|
130
|
+
| `height` | int | `800` | Viewport height in pixels |
|
|
131
|
+
| `full_page` | bool | `False` | Capture the full scrollable page |
|
|
132
|
+
| `quality` | int | `90` | JPEG/WebP quality (1-100) |
|
|
133
|
+
| `delay` | int | `0` | Wait (ms) after page load before capture |
|
|
134
|
+
| `dark_mode` | bool | `False` | Emulate dark color scheme |
|
|
135
|
+
| `block_ads` | bool | `True` | Block ad networks |
|
|
136
|
+
| `block_cookie_banners` | bool | `True` | Remove cookie consent banners |
|
|
137
|
+
| `device` | str | `None` | Device preset: `"iphone_14"`, `"iphone_15_pro"`, `"pixel_7"`, `"ipad_pro"`, `"macbook_pro"` |
|
|
138
|
+
| `hide_selectors` | str | `None` | Comma-separated CSS selectors to hide |
|
|
139
|
+
| `click_selector` | str | `None` | CSS selector to click before capture |
|
|
140
|
+
| `user_agent` | str | `None` | Custom user-agent string |
|
|
141
|
+
| `cache` | bool | `True` | Use CDN cache |
|
|
142
|
+
| `cache_ttl` | int | `86400` | Cache TTL in seconds |
|
|
143
|
+
| `response_type` | str | `None` | Set to `"json"` for metadata + base64 data URI |
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### `snap.sign(url, *, expires_in, **options)` -> `dict`
|
|
148
|
+
|
|
149
|
+
Generate a signed URL that can be used without an API key. Signing itself is free and does not count against your quota. One credit is consumed when the signed URL is rendered.
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
result = snap.sign(
|
|
153
|
+
"https://example.com",
|
|
154
|
+
expires_in=3600, # optional, seconds until expiry
|
|
155
|
+
format="png",
|
|
156
|
+
width=1280,
|
|
157
|
+
dark_mode=True,
|
|
158
|
+
)
|
|
159
|
+
print(result["signed_url"]) # use this in <img> tags, emails, etc.
|
|
160
|
+
print(result["expires_at"]) # ISO 8601 timestamp
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Options**
|
|
164
|
+
|
|
165
|
+
| Option | Type | Default | Description |
|
|
166
|
+
|--------|------|---------|-------------|
|
|
167
|
+
| `url` | str | (required) | URL to capture |
|
|
168
|
+
| `expires_in` | int | `None` | Seconds until the signed URL expires |
|
|
169
|
+
| `format` | str | `None` | `"png"`, `"jpeg"`, `"webp"`, or `"pdf"` |
|
|
170
|
+
| `width` | int | `None` | Viewport width |
|
|
171
|
+
| `height` | int | `None` | Viewport height |
|
|
172
|
+
| `full_page` | bool | `None` | Full-page capture |
|
|
173
|
+
| `quality` | int | `None` | JPEG/WebP quality |
|
|
174
|
+
| `delay` | int | `None` | Wait (ms) after load |
|
|
175
|
+
| `dark_mode` | bool | `None` | Emulate dark mode |
|
|
176
|
+
| `block_ads` | bool | `None` | Block ads |
|
|
177
|
+
| `block_cookie_banners` | bool | `None` | Remove cookie banners |
|
|
178
|
+
| `hide_selectors` | str | `None` | CSS selectors to hide |
|
|
179
|
+
| `click_selector` | str | `None` | CSS selector to click |
|
|
180
|
+
| `device` | str | `None` | Device preset |
|
|
181
|
+
| `user_agent` | str | `None` | Custom user-agent |
|
|
182
|
+
|
|
183
|
+
**Returns** `{ "signed_url": str, "expires_at": str, "expires_in": int }`
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
### `snap.extract(url, *, type, **options)` -> `dict`
|
|
188
|
+
|
|
189
|
+
Extract content from a web page. Six extraction types are available:
|
|
190
|
+
|
|
191
|
+
| Type | Description |
|
|
192
|
+
|------|-------------|
|
|
193
|
+
| `"markdown"` | Page content converted to Markdown (default) |
|
|
194
|
+
| `"text"` | Plain text, all tags stripped |
|
|
195
|
+
| `"html"` | Cleaned HTML |
|
|
196
|
+
| `"article"` | Article body extracted via Readability |
|
|
197
|
+
| `"links"` | All links on the page |
|
|
198
|
+
| `"metadata"` | Title, description, Open Graph tags, etc. |
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
# Extract as Markdown
|
|
202
|
+
result = snap.extract("https://example.com", type="markdown")
|
|
203
|
+
print(result["content"])
|
|
204
|
+
print(result["wordCount"])
|
|
205
|
+
|
|
206
|
+
# Extract just the article body
|
|
207
|
+
article = snap.extract("https://example.com/blog/post", type="article")
|
|
208
|
+
|
|
209
|
+
# Extract metadata
|
|
210
|
+
meta = snap.extract("https://example.com", type="metadata")
|
|
211
|
+
|
|
212
|
+
# Scope extraction to a specific element
|
|
213
|
+
result = snap.extract(
|
|
214
|
+
"https://example.com",
|
|
215
|
+
type="text",
|
|
216
|
+
selector="#main-content",
|
|
217
|
+
max_length=5000,
|
|
218
|
+
)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Options**
|
|
222
|
+
|
|
223
|
+
| Option | Type | Default | Description |
|
|
224
|
+
|--------|------|---------|-------------|
|
|
225
|
+
| `url` | str | (required) | URL to extract from |
|
|
226
|
+
| `type` | str | `"markdown"` | Extraction type (see table above) |
|
|
227
|
+
| `selector` | str | `None` | CSS selector to scope extraction |
|
|
228
|
+
| `block_ads` | bool | `None` | Block ads before extraction |
|
|
229
|
+
| `block_cookie_banners` | bool | `None` | Remove cookie banners |
|
|
230
|
+
| `delay` | int | `None` | Wait (ms) after page load |
|
|
231
|
+
| `max_length` | int | `None` | Truncate content to this many characters |
|
|
232
|
+
| `cache` | bool | `None` | Use cache |
|
|
233
|
+
| `cache_ttl` | int | `None` | Cache TTL in seconds |
|
|
234
|
+
|
|
235
|
+
**Returns** `{ "url": str, "type": str, "content": ..., "wordCount": int, "processingTimeMs": int }`
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
### `snap.batch(urls, **options)` -> `dict`
|
|
240
|
+
|
|
241
|
+
Create a batch screenshot job for 1 to 50 URLs. The call returns immediately with a job ID. Each URL consumes one credit; credits for failed URLs are rolled back.
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
job = snap.batch(
|
|
245
|
+
["https://example.com", "https://example.org", "https://example.net"],
|
|
246
|
+
format="png",
|
|
247
|
+
width=1280,
|
|
248
|
+
dark_mode=True,
|
|
249
|
+
)
|
|
250
|
+
print(job["job_id"]) # use this to poll status
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Options**: same screenshot options as `capture()` (except `url`, `html`, `markdown`, `response_type`, `cache`, `cache_ttl`). These options apply to every URL in the batch.
|
|
254
|
+
|
|
255
|
+
**Returns** `{ "job_id": str, "status": str, "total": int, ... }`
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
### `snap.get_batch_status(job_id)` -> `dict`
|
|
260
|
+
|
|
261
|
+
Poll the status of a batch job. Keep polling until `status` is `"completed"` or `"failed"`.
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
import time
|
|
265
|
+
|
|
266
|
+
job = snap.batch(["https://example.com", "https://example.org"])
|
|
267
|
+
|
|
268
|
+
while True:
|
|
269
|
+
status = snap.get_batch_status(job["job_id"])
|
|
270
|
+
print(f"{status['status']} - {status.get('completed', 0)}/{status['total']}")
|
|
271
|
+
if status["status"] in ("completed", "failed"):
|
|
272
|
+
break
|
|
273
|
+
time.sleep(2)
|
|
274
|
+
|
|
275
|
+
# Download results
|
|
276
|
+
for result in status["results"]:
|
|
277
|
+
print(result["url"], result["download_url"])
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
### `snap.create_webhook(url, events)` -> `dict`
|
|
283
|
+
|
|
284
|
+
Register a webhook endpoint to receive event notifications. Maximum 5 webhooks per account.
|
|
285
|
+
|
|
286
|
+
Available events: `screenshot.completed`, `quota.warning`, `quota.exceeded`
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
webhook = snap.create_webhook(
|
|
290
|
+
url="https://yoursite.com/webhooks/snaprender",
|
|
291
|
+
events=["screenshot.completed", "quota.warning"],
|
|
292
|
+
)
|
|
293
|
+
print(webhook["id"])
|
|
294
|
+
print(webhook["secret"]) # save this for signature verification
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
### `snap.list_webhooks()` -> `list[dict]`
|
|
300
|
+
|
|
301
|
+
List all webhooks registered on your account.
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
webhooks = snap.list_webhooks()
|
|
305
|
+
for wh in webhooks:
|
|
306
|
+
print(wh["id"], wh["url"], wh["events"])
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
### `snap.delete_webhook(webhook_id)` -> `None`
|
|
312
|
+
|
|
313
|
+
Delete a webhook by its ID.
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
snap.delete_webhook("wh_abc123")
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
### `snap.test_webhook(webhook_id)` -> `dict`
|
|
322
|
+
|
|
323
|
+
Send a test payload to a webhook endpoint. Useful for verifying your handler works before relying on real events.
|
|
324
|
+
|
|
325
|
+
```python
|
|
326
|
+
result = snap.test_webhook("wh_abc123")
|
|
327
|
+
print(result) # delivery status
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
### `SnapRender.verify_webhook_signature(payload, signature, secret)` -> `bool`
|
|
333
|
+
|
|
334
|
+
Static method. Verify the HMAC-SHA256 signature of an incoming webhook payload. Use this in your webhook handler to confirm the request was sent by SnapRender.
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
from snaprender import SnapRender
|
|
338
|
+
|
|
339
|
+
# In your webhook handler (e.g., Flask)
|
|
340
|
+
@app.route("/webhooks/snaprender", methods=["POST"])
|
|
341
|
+
def handle_webhook():
|
|
342
|
+
payload = request.get_data(as_text=True)
|
|
343
|
+
signature = request.headers.get("X-Signature")
|
|
344
|
+
secret = "whsec_..." # from create_webhook response
|
|
345
|
+
|
|
346
|
+
if not SnapRender.verify_webhook_signature(payload, signature, secret):
|
|
347
|
+
return "Invalid signature", 403
|
|
348
|
+
|
|
349
|
+
event = request.get_json()
|
|
350
|
+
print(event["type"], event["data"])
|
|
351
|
+
return "OK", 200
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
### `snap.info(url)` -> `dict`
|
|
357
|
+
|
|
358
|
+
Check if a screenshot is cached for a given URL, without capturing it. No credits are consumed.
|
|
359
|
+
|
|
360
|
+
```python
|
|
361
|
+
info = snap.info("https://example.com")
|
|
362
|
+
print(info["cached"]) # True or False
|
|
363
|
+
print(info["expires_at"]) # ISO timestamp (when cached)
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**Returns** `{ "url": str, "cached": bool, "cache_key": str | None, "cached_at": str | None, "expires_at": str | None, "content_type": str | None }`
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
### `snap.usage()` -> `dict`
|
|
371
|
+
|
|
372
|
+
Get the current billing period's usage summary.
|
|
373
|
+
|
|
374
|
+
```python
|
|
375
|
+
usage = snap.usage()
|
|
376
|
+
print(f"{usage['used']}/{usage['limit']} screenshots used")
|
|
377
|
+
print(f"Plan: {usage['plan']}, Remaining: {usage['remaining']}")
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**Returns** `{ "plan": str, "used": int, "limit": int, "remaining": int, "period": dict }`
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
### `snap.usage_daily(days=30)` -> `dict`
|
|
385
|
+
|
|
386
|
+
Get a day-by-day usage breakdown.
|
|
387
|
+
|
|
388
|
+
```python
|
|
389
|
+
daily = snap.usage_daily(days=7)
|
|
390
|
+
for day in daily["daily"]:
|
|
391
|
+
print(day["date"], day["count"])
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
| Parameter | Type | Default | Description |
|
|
395
|
+
|-----------|------|---------|-------------|
|
|
396
|
+
| `days` | int | `30` | Number of days to include |
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## LangChain Integration
|
|
401
|
+
|
|
402
|
+
Use SnapRender as a [LangChain](https://python.langchain.com/) tool for AI agents:
|
|
403
|
+
|
|
404
|
+
```python
|
|
405
|
+
import os
|
|
406
|
+
from langchain_core.tools import tool
|
|
407
|
+
from snaprender import SnapRender
|
|
408
|
+
|
|
409
|
+
client = SnapRender(api_key=os.environ["SNAPRENDER_API_KEY"])
|
|
410
|
+
|
|
411
|
+
@tool
|
|
412
|
+
def take_screenshot(url: str, format: str = "png", dark_mode: bool = False) -> str:
|
|
413
|
+
"""Capture a screenshot of any website URL. Returns base64 data URI."""
|
|
414
|
+
result = client.capture(
|
|
415
|
+
url, format=format, dark_mode=dark_mode, response_type="json"
|
|
416
|
+
)
|
|
417
|
+
return result["image"]
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Works with LangGraph, CrewAI, and any framework that supports LangChain tools.
|
|
421
|
+
|
|
422
|
+
## Error Handling
|
|
423
|
+
|
|
424
|
+
All API errors raise `SnapRenderError` with `code`, `status`, and a human-readable message.
|
|
425
|
+
|
|
426
|
+
```python
|
|
427
|
+
from snaprender import SnapRender, SnapRenderError
|
|
428
|
+
|
|
429
|
+
snap = SnapRender(api_key="sk_live_...")
|
|
430
|
+
|
|
431
|
+
try:
|
|
432
|
+
snap.capture("https://example.com")
|
|
433
|
+
except SnapRenderError as e:
|
|
434
|
+
print(e.code) # "QUOTA_EXCEEDED"
|
|
435
|
+
print(e.status) # 429
|
|
436
|
+
print(e) # "Monthly quota exceeded"
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Common error codes: `INVALID_API_KEY`, `QUOTA_EXCEEDED`, `INVALID_URL`, `VALIDATION_ERROR`, `SCREENSHOT_FAILED`.
|
|
440
|
+
|
|
441
|
+
## Links
|
|
442
|
+
|
|
443
|
+
- [Documentation](https://snap-render.com/docs)
|
|
444
|
+
- [Pricing](https://snap-render.com/pricing)
|
|
445
|
+
- [Dashboard](https://app.snap-render.com/dashboard)
|
|
446
|
+
- [Blog](https://snap-render.com/blog)
|
|
447
|
+
- [GitHub](https://github.com/User0856/snaprender)
|
|
448
|
+
|
|
449
|
+
## License
|
|
450
|
+
|
|
451
|
+
MIT
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
# snaprender
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/snaprender/)
|
|
4
|
+
[](https://pypi.org/project/snaprender/)
|
|
5
|
+
[](https://github.com/User0856/snaprender/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
Official Python SDK for the [SnapRender](https://snap-render.com) Screenshot API. Capture pixel-perfect screenshots, extract page content, run batch jobs, and manage webhooks with a single library.
|
|
8
|
+
|
|
9
|
+
**500 free screenshots/month. [Get your API key](https://snap-render.com/auth/signup?ref=pypi)**
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Multiple output formats** : PNG, JPEG, WebP, and PDF
|
|
14
|
+
- **Three input modes** : URL, raw HTML, or Markdown
|
|
15
|
+
- **Signed URLs** : generate pre-signed screenshot URLs (no API key needed at render time)
|
|
16
|
+
- **Content extraction** : pull Markdown, plain text, HTML, article, links, or metadata from any page
|
|
17
|
+
- **Batch screenshots** : capture up to 50 URLs in a single job
|
|
18
|
+
- **Webhooks** : receive real-time notifications for screenshot and quota events
|
|
19
|
+
- **Device emulation** : iPhone, iPad, Pixel, and more presets
|
|
20
|
+
- **Dark mode** : capture pages with `prefers-color-scheme: dark`
|
|
21
|
+
- **Ad blocking** : remove ads automatically before capture
|
|
22
|
+
- **Cookie banner removal** : clean screenshots without consent popups
|
|
23
|
+
- **Smart caching** : configurable CDN cache with TTL control
|
|
24
|
+
- **Full-page captures** : screenshot the entire scrollable page
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install snaprender
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from snaprender import SnapRender
|
|
36
|
+
|
|
37
|
+
snap = SnapRender(api_key="sk_live_...")
|
|
38
|
+
|
|
39
|
+
# Capture by URL
|
|
40
|
+
image = snap.capture("https://example.com")
|
|
41
|
+
with open("screenshot.png", "wb") as f:
|
|
42
|
+
f.write(image)
|
|
43
|
+
|
|
44
|
+
# Capture from raw HTML
|
|
45
|
+
image = snap.capture(html="<h1>Hello World</h1>")
|
|
46
|
+
|
|
47
|
+
# Capture from Markdown
|
|
48
|
+
image = snap.capture(markdown="# Hello World\nSome **bold** text.")
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Context Manager
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
with SnapRender(api_key="sk_live_...") as snap:
|
|
55
|
+
image = snap.capture("https://example.com")
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The HTTP connection is closed automatically when the block exits.
|
|
59
|
+
|
|
60
|
+
## API Reference
|
|
61
|
+
|
|
62
|
+
### Constructor
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
SnapRender(api_key, base_url="https://app.snap-render.com", timeout=60.0)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
| Parameter | Type | Default | Description |
|
|
69
|
+
|-----------|------|---------|-------------|
|
|
70
|
+
| `api_key` | str | (required) | Your SnapRender API key (`sk_live_...`) |
|
|
71
|
+
| `base_url` | str | `"https://app.snap-render.com"` | API base URL |
|
|
72
|
+
| `timeout` | float | `60.0` | Request timeout in seconds |
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
### `snap.capture(url, *, html, markdown, **options)` -> `bytes | dict`
|
|
77
|
+
|
|
78
|
+
Take a screenshot. Provide exactly **one** of `url`, `html`, or `markdown`.
|
|
79
|
+
|
|
80
|
+
- When `url` is provided alone, the request is sent as GET with query parameters.
|
|
81
|
+
- When `html` or `markdown` is provided, the request is sent as POST with a JSON body.
|
|
82
|
+
- Returns raw image bytes by default. Set `response_type="json"` to get a dict with metadata and a base64 data URI.
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
# URL with options
|
|
86
|
+
jpg = snap.capture(
|
|
87
|
+
"https://example.com",
|
|
88
|
+
format="jpeg",
|
|
89
|
+
width=1920,
|
|
90
|
+
height=1080,
|
|
91
|
+
full_page=True,
|
|
92
|
+
dark_mode=True,
|
|
93
|
+
quality=95,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# HTML input
|
|
97
|
+
png = snap.capture(html="<h1 style='color:red'>Red Title</h1>", width=800)
|
|
98
|
+
|
|
99
|
+
# Markdown input
|
|
100
|
+
png = snap.capture(markdown="# Report\n\n| Col A | Col B |\n|-------|-------|\n| 1 | 2 |")
|
|
101
|
+
|
|
102
|
+
# JSON response (base64 data URI + metadata)
|
|
103
|
+
result = snap.capture("https://example.com", response_type="json")
|
|
104
|
+
print(result["image"]) # data:image/png;base64,...
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Options**
|
|
108
|
+
|
|
109
|
+
| Option | Type | Default | Description |
|
|
110
|
+
|--------|------|---------|-------------|
|
|
111
|
+
| `url` | str | `None` | URL to capture |
|
|
112
|
+
| `html` | str | `None` | Raw HTML to render |
|
|
113
|
+
| `markdown` | str | `None` | Markdown to render |
|
|
114
|
+
| `format` | str | `"png"` | `"png"`, `"jpeg"`, `"webp"`, or `"pdf"` |
|
|
115
|
+
| `width` | int | `1280` | Viewport width in pixels |
|
|
116
|
+
| `height` | int | `800` | Viewport height in pixels |
|
|
117
|
+
| `full_page` | bool | `False` | Capture the full scrollable page |
|
|
118
|
+
| `quality` | int | `90` | JPEG/WebP quality (1-100) |
|
|
119
|
+
| `delay` | int | `0` | Wait (ms) after page load before capture |
|
|
120
|
+
| `dark_mode` | bool | `False` | Emulate dark color scheme |
|
|
121
|
+
| `block_ads` | bool | `True` | Block ad networks |
|
|
122
|
+
| `block_cookie_banners` | bool | `True` | Remove cookie consent banners |
|
|
123
|
+
| `device` | str | `None` | Device preset: `"iphone_14"`, `"iphone_15_pro"`, `"pixel_7"`, `"ipad_pro"`, `"macbook_pro"` |
|
|
124
|
+
| `hide_selectors` | str | `None` | Comma-separated CSS selectors to hide |
|
|
125
|
+
| `click_selector` | str | `None` | CSS selector to click before capture |
|
|
126
|
+
| `user_agent` | str | `None` | Custom user-agent string |
|
|
127
|
+
| `cache` | bool | `True` | Use CDN cache |
|
|
128
|
+
| `cache_ttl` | int | `86400` | Cache TTL in seconds |
|
|
129
|
+
| `response_type` | str | `None` | Set to `"json"` for metadata + base64 data URI |
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### `snap.sign(url, *, expires_in, **options)` -> `dict`
|
|
134
|
+
|
|
135
|
+
Generate a signed URL that can be used without an API key. Signing itself is free and does not count against your quota. One credit is consumed when the signed URL is rendered.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
result = snap.sign(
|
|
139
|
+
"https://example.com",
|
|
140
|
+
expires_in=3600, # optional, seconds until expiry
|
|
141
|
+
format="png",
|
|
142
|
+
width=1280,
|
|
143
|
+
dark_mode=True,
|
|
144
|
+
)
|
|
145
|
+
print(result["signed_url"]) # use this in <img> tags, emails, etc.
|
|
146
|
+
print(result["expires_at"]) # ISO 8601 timestamp
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Options**
|
|
150
|
+
|
|
151
|
+
| Option | Type | Default | Description |
|
|
152
|
+
|--------|------|---------|-------------|
|
|
153
|
+
| `url` | str | (required) | URL to capture |
|
|
154
|
+
| `expires_in` | int | `None` | Seconds until the signed URL expires |
|
|
155
|
+
| `format` | str | `None` | `"png"`, `"jpeg"`, `"webp"`, or `"pdf"` |
|
|
156
|
+
| `width` | int | `None` | Viewport width |
|
|
157
|
+
| `height` | int | `None` | Viewport height |
|
|
158
|
+
| `full_page` | bool | `None` | Full-page capture |
|
|
159
|
+
| `quality` | int | `None` | JPEG/WebP quality |
|
|
160
|
+
| `delay` | int | `None` | Wait (ms) after load |
|
|
161
|
+
| `dark_mode` | bool | `None` | Emulate dark mode |
|
|
162
|
+
| `block_ads` | bool | `None` | Block ads |
|
|
163
|
+
| `block_cookie_banners` | bool | `None` | Remove cookie banners |
|
|
164
|
+
| `hide_selectors` | str | `None` | CSS selectors to hide |
|
|
165
|
+
| `click_selector` | str | `None` | CSS selector to click |
|
|
166
|
+
| `device` | str | `None` | Device preset |
|
|
167
|
+
| `user_agent` | str | `None` | Custom user-agent |
|
|
168
|
+
|
|
169
|
+
**Returns** `{ "signed_url": str, "expires_at": str, "expires_in": int }`
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
### `snap.extract(url, *, type, **options)` -> `dict`
|
|
174
|
+
|
|
175
|
+
Extract content from a web page. Six extraction types are available:
|
|
176
|
+
|
|
177
|
+
| Type | Description |
|
|
178
|
+
|------|-------------|
|
|
179
|
+
| `"markdown"` | Page content converted to Markdown (default) |
|
|
180
|
+
| `"text"` | Plain text, all tags stripped |
|
|
181
|
+
| `"html"` | Cleaned HTML |
|
|
182
|
+
| `"article"` | Article body extracted via Readability |
|
|
183
|
+
| `"links"` | All links on the page |
|
|
184
|
+
| `"metadata"` | Title, description, Open Graph tags, etc. |
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
# Extract as Markdown
|
|
188
|
+
result = snap.extract("https://example.com", type="markdown")
|
|
189
|
+
print(result["content"])
|
|
190
|
+
print(result["wordCount"])
|
|
191
|
+
|
|
192
|
+
# Extract just the article body
|
|
193
|
+
article = snap.extract("https://example.com/blog/post", type="article")
|
|
194
|
+
|
|
195
|
+
# Extract metadata
|
|
196
|
+
meta = snap.extract("https://example.com", type="metadata")
|
|
197
|
+
|
|
198
|
+
# Scope extraction to a specific element
|
|
199
|
+
result = snap.extract(
|
|
200
|
+
"https://example.com",
|
|
201
|
+
type="text",
|
|
202
|
+
selector="#main-content",
|
|
203
|
+
max_length=5000,
|
|
204
|
+
)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Options**
|
|
208
|
+
|
|
209
|
+
| Option | Type | Default | Description |
|
|
210
|
+
|--------|------|---------|-------------|
|
|
211
|
+
| `url` | str | (required) | URL to extract from |
|
|
212
|
+
| `type` | str | `"markdown"` | Extraction type (see table above) |
|
|
213
|
+
| `selector` | str | `None` | CSS selector to scope extraction |
|
|
214
|
+
| `block_ads` | bool | `None` | Block ads before extraction |
|
|
215
|
+
| `block_cookie_banners` | bool | `None` | Remove cookie banners |
|
|
216
|
+
| `delay` | int | `None` | Wait (ms) after page load |
|
|
217
|
+
| `max_length` | int | `None` | Truncate content to this many characters |
|
|
218
|
+
| `cache` | bool | `None` | Use cache |
|
|
219
|
+
| `cache_ttl` | int | `None` | Cache TTL in seconds |
|
|
220
|
+
|
|
221
|
+
**Returns** `{ "url": str, "type": str, "content": ..., "wordCount": int, "processingTimeMs": int }`
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
### `snap.batch(urls, **options)` -> `dict`
|
|
226
|
+
|
|
227
|
+
Create a batch screenshot job for 1 to 50 URLs. The call returns immediately with a job ID. Each URL consumes one credit; credits for failed URLs are rolled back.
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
job = snap.batch(
|
|
231
|
+
["https://example.com", "https://example.org", "https://example.net"],
|
|
232
|
+
format="png",
|
|
233
|
+
width=1280,
|
|
234
|
+
dark_mode=True,
|
|
235
|
+
)
|
|
236
|
+
print(job["job_id"]) # use this to poll status
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Options**: same screenshot options as `capture()` (except `url`, `html`, `markdown`, `response_type`, `cache`, `cache_ttl`). These options apply to every URL in the batch.
|
|
240
|
+
|
|
241
|
+
**Returns** `{ "job_id": str, "status": str, "total": int, ... }`
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
### `snap.get_batch_status(job_id)` -> `dict`
|
|
246
|
+
|
|
247
|
+
Poll the status of a batch job. Keep polling until `status` is `"completed"` or `"failed"`.
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
import time
|
|
251
|
+
|
|
252
|
+
job = snap.batch(["https://example.com", "https://example.org"])
|
|
253
|
+
|
|
254
|
+
while True:
|
|
255
|
+
status = snap.get_batch_status(job["job_id"])
|
|
256
|
+
print(f"{status['status']} - {status.get('completed', 0)}/{status['total']}")
|
|
257
|
+
if status["status"] in ("completed", "failed"):
|
|
258
|
+
break
|
|
259
|
+
time.sleep(2)
|
|
260
|
+
|
|
261
|
+
# Download results
|
|
262
|
+
for result in status["results"]:
|
|
263
|
+
print(result["url"], result["download_url"])
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
### `snap.create_webhook(url, events)` -> `dict`
|
|
269
|
+
|
|
270
|
+
Register a webhook endpoint to receive event notifications. Maximum 5 webhooks per account.
|
|
271
|
+
|
|
272
|
+
Available events: `screenshot.completed`, `quota.warning`, `quota.exceeded`
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
webhook = snap.create_webhook(
|
|
276
|
+
url="https://yoursite.com/webhooks/snaprender",
|
|
277
|
+
events=["screenshot.completed", "quota.warning"],
|
|
278
|
+
)
|
|
279
|
+
print(webhook["id"])
|
|
280
|
+
print(webhook["secret"]) # save this for signature verification
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
### `snap.list_webhooks()` -> `list[dict]`
|
|
286
|
+
|
|
287
|
+
List all webhooks registered on your account.
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
webhooks = snap.list_webhooks()
|
|
291
|
+
for wh in webhooks:
|
|
292
|
+
print(wh["id"], wh["url"], wh["events"])
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
### `snap.delete_webhook(webhook_id)` -> `None`
|
|
298
|
+
|
|
299
|
+
Delete a webhook by its ID.
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
snap.delete_webhook("wh_abc123")
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
### `snap.test_webhook(webhook_id)` -> `dict`
|
|
308
|
+
|
|
309
|
+
Send a test payload to a webhook endpoint. Useful for verifying your handler works before relying on real events.
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
result = snap.test_webhook("wh_abc123")
|
|
313
|
+
print(result) # delivery status
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
### `SnapRender.verify_webhook_signature(payload, signature, secret)` -> `bool`
|
|
319
|
+
|
|
320
|
+
Static method. Verify the HMAC-SHA256 signature of an incoming webhook payload. Use this in your webhook handler to confirm the request was sent by SnapRender.
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
from snaprender import SnapRender
|
|
324
|
+
|
|
325
|
+
# In your webhook handler (e.g., Flask)
|
|
326
|
+
@app.route("/webhooks/snaprender", methods=["POST"])
|
|
327
|
+
def handle_webhook():
|
|
328
|
+
payload = request.get_data(as_text=True)
|
|
329
|
+
signature = request.headers.get("X-Signature")
|
|
330
|
+
secret = "whsec_..." # from create_webhook response
|
|
331
|
+
|
|
332
|
+
if not SnapRender.verify_webhook_signature(payload, signature, secret):
|
|
333
|
+
return "Invalid signature", 403
|
|
334
|
+
|
|
335
|
+
event = request.get_json()
|
|
336
|
+
print(event["type"], event["data"])
|
|
337
|
+
return "OK", 200
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
### `snap.info(url)` -> `dict`
|
|
343
|
+
|
|
344
|
+
Check if a screenshot is cached for a given URL, without capturing it. No credits are consumed.
|
|
345
|
+
|
|
346
|
+
```python
|
|
347
|
+
info = snap.info("https://example.com")
|
|
348
|
+
print(info["cached"]) # True or False
|
|
349
|
+
print(info["expires_at"]) # ISO timestamp (when cached)
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Returns** `{ "url": str, "cached": bool, "cache_key": str | None, "cached_at": str | None, "expires_at": str | None, "content_type": str | None }`
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
### `snap.usage()` -> `dict`
|
|
357
|
+
|
|
358
|
+
Get the current billing period's usage summary.
|
|
359
|
+
|
|
360
|
+
```python
|
|
361
|
+
usage = snap.usage()
|
|
362
|
+
print(f"{usage['used']}/{usage['limit']} screenshots used")
|
|
363
|
+
print(f"Plan: {usage['plan']}, Remaining: {usage['remaining']}")
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**Returns** `{ "plan": str, "used": int, "limit": int, "remaining": int, "period": dict }`
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
### `snap.usage_daily(days=30)` -> `dict`
|
|
371
|
+
|
|
372
|
+
Get a day-by-day usage breakdown.
|
|
373
|
+
|
|
374
|
+
```python
|
|
375
|
+
daily = snap.usage_daily(days=7)
|
|
376
|
+
for day in daily["daily"]:
|
|
377
|
+
print(day["date"], day["count"])
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
| Parameter | Type | Default | Description |
|
|
381
|
+
|-----------|------|---------|-------------|
|
|
382
|
+
| `days` | int | `30` | Number of days to include |
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## LangChain Integration
|
|
387
|
+
|
|
388
|
+
Use SnapRender as a [LangChain](https://python.langchain.com/) tool for AI agents:
|
|
389
|
+
|
|
390
|
+
```python
|
|
391
|
+
import os
|
|
392
|
+
from langchain_core.tools import tool
|
|
393
|
+
from snaprender import SnapRender
|
|
394
|
+
|
|
395
|
+
client = SnapRender(api_key=os.environ["SNAPRENDER_API_KEY"])
|
|
396
|
+
|
|
397
|
+
@tool
|
|
398
|
+
def take_screenshot(url: str, format: str = "png", dark_mode: bool = False) -> str:
|
|
399
|
+
"""Capture a screenshot of any website URL. Returns base64 data URI."""
|
|
400
|
+
result = client.capture(
|
|
401
|
+
url, format=format, dark_mode=dark_mode, response_type="json"
|
|
402
|
+
)
|
|
403
|
+
return result["image"]
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Works with LangGraph, CrewAI, and any framework that supports LangChain tools.
|
|
407
|
+
|
|
408
|
+
## Error Handling
|
|
409
|
+
|
|
410
|
+
All API errors raise `SnapRenderError` with `code`, `status`, and a human-readable message.
|
|
411
|
+
|
|
412
|
+
```python
|
|
413
|
+
from snaprender import SnapRender, SnapRenderError
|
|
414
|
+
|
|
415
|
+
snap = SnapRender(api_key="sk_live_...")
|
|
416
|
+
|
|
417
|
+
try:
|
|
418
|
+
snap.capture("https://example.com")
|
|
419
|
+
except SnapRenderError as e:
|
|
420
|
+
print(e.code) # "QUOTA_EXCEEDED"
|
|
421
|
+
print(e.status) # 429
|
|
422
|
+
print(e) # "Monthly quota exceeded"
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Common error codes: `INVALID_API_KEY`, `QUOTA_EXCEEDED`, `INVALID_URL`, `VALIDATION_ERROR`, `SCREENSHOT_FAILED`.
|
|
426
|
+
|
|
427
|
+
## Links
|
|
428
|
+
|
|
429
|
+
- [Documentation](https://snap-render.com/docs)
|
|
430
|
+
- [Pricing](https://snap-render.com/pricing)
|
|
431
|
+
- [Dashboard](https://app.snap-render.com/dashboard)
|
|
432
|
+
- [Blog](https://snap-render.com/blog)
|
|
433
|
+
- [GitHub](https://github.com/User0856/snaprender)
|
|
434
|
+
|
|
435
|
+
## License
|
|
436
|
+
|
|
437
|
+
MIT
|
snaprender-0.7.0/PKG-INFO
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: snaprender
|
|
3
|
-
Version: 0.7.0
|
|
4
|
-
Summary: Official Python SDK for SnapRender Screenshot API
|
|
5
|
-
Project-URL: Homepage, https://snap-render.com
|
|
6
|
-
Project-URL: Documentation, https://snap-render.com/docs
|
|
7
|
-
Project-URL: Repository, https://github.com/User0856/snaprender
|
|
8
|
-
License: MIT
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Keywords: api,capture,screenshot,snaprender,webpage
|
|
11
|
-
Requires-Python: >=3.8
|
|
12
|
-
Requires-Dist: httpx>=0.24
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
|
|
15
|
-
# snaprender
|
|
16
|
-
|
|
17
|
-
[](https://pypi.org/project/snaprender/)
|
|
18
|
-
[](https://pypi.org/project/snaprender/)
|
|
19
|
-
[](https://github.com/User0856/snaprender/blob/main/LICENSE)
|
|
20
|
-
|
|
21
|
-
Official Python SDK for the [SnapRender](https://snap-render.com) Screenshot API — capture pixel-perfect screenshots of any webpage with a single API call.
|
|
22
|
-
|
|
23
|
-
**500 free screenshots/month — [Get your API key](https://snap-render.com/auth/signup?ref=pypi)**
|
|
24
|
-
|
|
25
|
-
## Features
|
|
26
|
-
|
|
27
|
-
- **Multiple output formats** — PNG, JPEG, WebP, and PDF
|
|
28
|
-
- **Device emulation** — iPhone, iPad, Pixel, and more presets
|
|
29
|
-
- **Dark mode** — Capture pages with `prefers-color-scheme: dark`
|
|
30
|
-
- **Ad blocking** — Remove ads automatically before capture
|
|
31
|
-
- **Cookie banner removal** — Clean screenshots without consent popups
|
|
32
|
-
- **Smart caching** — Configurable CDN cache with TTL control
|
|
33
|
-
- **Full-page captures** — Screenshot the entire scrollable page
|
|
34
|
-
|
|
35
|
-
## Install
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
pip install snaprender
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Quick Start
|
|
42
|
-
|
|
43
|
-
```python
|
|
44
|
-
from snaprender import SnapRender
|
|
45
|
-
|
|
46
|
-
snap = SnapRender(api_key="sk_live_...")
|
|
47
|
-
|
|
48
|
-
# Capture a screenshot
|
|
49
|
-
image = snap.capture("https://example.com")
|
|
50
|
-
with open("screenshot.png", "wb") as f:
|
|
51
|
-
f.write(image)
|
|
52
|
-
|
|
53
|
-
# Capture with options
|
|
54
|
-
jpg = snap.capture(
|
|
55
|
-
"https://example.com",
|
|
56
|
-
format="jpeg",
|
|
57
|
-
width=1920,
|
|
58
|
-
height=1080,
|
|
59
|
-
full_page=True,
|
|
60
|
-
dark_mode=True,
|
|
61
|
-
quality=95,
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
# Check cache status
|
|
65
|
-
info = snap.info("https://example.com")
|
|
66
|
-
print(info["cached"]) # True/False
|
|
67
|
-
print(info["expires_at"]) # ISO date string (when cached)
|
|
68
|
-
|
|
69
|
-
# Get usage
|
|
70
|
-
usage = snap.usage()
|
|
71
|
-
print(f"{usage['used']}/{usage['limit']} screenshots used")
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Context Manager
|
|
75
|
-
|
|
76
|
-
```python
|
|
77
|
-
with SnapRender(api_key="sk_live_...") as snap:
|
|
78
|
-
image = snap.capture("https://example.com")
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## API
|
|
82
|
-
|
|
83
|
-
### `SnapRender(api_key, base_url="https://app.snap-render.com", timeout=60.0)`
|
|
84
|
-
|
|
85
|
-
### `snap.capture(url, **options)` -> `bytes`
|
|
86
|
-
|
|
87
|
-
| Option | Type | Default | Description |
|
|
88
|
-
|--------|------|---------|-------------|
|
|
89
|
-
| `url` | str | — | URL to capture (required) |
|
|
90
|
-
| `format` | str | `"png"` | `"png"`, `"jpeg"`, `"webp"`, or `"pdf"` |
|
|
91
|
-
| `width` | int | `1280` | Viewport width |
|
|
92
|
-
| `height` | int | `800` | Viewport height |
|
|
93
|
-
| `full_page` | bool | `False` | Capture full scrollable page |
|
|
94
|
-
| `quality` | int | `90` | JPEG/WebP quality (1-100) |
|
|
95
|
-
| `delay` | int | `0` | Wait ms after page load |
|
|
96
|
-
| `dark_mode` | bool | `False` | Emulate dark mode |
|
|
97
|
-
| `block_ads` | bool | `True` | Block ad networks |
|
|
98
|
-
| `block_cookie_banners` | bool | `True` | Remove cookie banners |
|
|
99
|
-
| `device` | str | — | Device preset (`"iphone_14"`, `"iphone_15_pro"`, `"pixel_7"`, `"ipad_pro"`, `"macbook_pro"`) |
|
|
100
|
-
| `hide_selectors` | str | — | Comma-separated CSS selectors to hide |
|
|
101
|
-
| `click_selector` | str | — | CSS selector to click before capture |
|
|
102
|
-
| `user_agent` | str | — | Custom user agent string |
|
|
103
|
-
| `cache` | bool | `True` | Use cache |
|
|
104
|
-
| `cache_ttl` | int | `86400` | Cache TTL in seconds |
|
|
105
|
-
| `response_type` | str | — | `"json"` returns metadata + base64 data URI |
|
|
106
|
-
|
|
107
|
-
### `snap.info(url)` -> `dict`
|
|
108
|
-
|
|
109
|
-
Check if a screenshot is cached without capturing. Returns `{ url, cached, cache_key, cached_at, expires_at, content_type }`.
|
|
110
|
-
|
|
111
|
-
### `snap.usage()` -> `dict`
|
|
112
|
-
|
|
113
|
-
Get current month's usage.
|
|
114
|
-
|
|
115
|
-
### `snap.usage_daily(days=30)` -> `dict`
|
|
116
|
-
|
|
117
|
-
Get daily usage breakdown (default 30 days).
|
|
118
|
-
|
|
119
|
-
## LangChain Integration
|
|
120
|
-
|
|
121
|
-
Use SnapRender as a [LangChain](https://python.langchain.com/) tool for AI agents:
|
|
122
|
-
|
|
123
|
-
```python
|
|
124
|
-
import os
|
|
125
|
-
from langchain_core.tools import tool
|
|
126
|
-
from snaprender import SnapRender
|
|
127
|
-
|
|
128
|
-
client = SnapRender(api_key=os.environ["SNAPRENDER_API_KEY"])
|
|
129
|
-
|
|
130
|
-
@tool
|
|
131
|
-
def take_screenshot(url: str, format: str = "png", dark_mode: bool = False) -> str:
|
|
132
|
-
"""Capture a screenshot of any website URL. Returns base64 data URI."""
|
|
133
|
-
result = client.capture(
|
|
134
|
-
url, format=format, dark_mode=dark_mode, response_type="json"
|
|
135
|
-
)
|
|
136
|
-
return result["image"]
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
Works with LangGraph, CrewAI, and any framework that supports LangChain tools.
|
|
140
|
-
|
|
141
|
-
## Error Handling
|
|
142
|
-
|
|
143
|
-
```python
|
|
144
|
-
from snaprender import SnapRender, SnapRenderError
|
|
145
|
-
|
|
146
|
-
snap = SnapRender(api_key="sk_live_...")
|
|
147
|
-
|
|
148
|
-
try:
|
|
149
|
-
snap.capture("https://example.com")
|
|
150
|
-
except SnapRenderError as e:
|
|
151
|
-
print(e.code) # "QUOTA_EXCEEDED"
|
|
152
|
-
print(e.status) # 429
|
|
153
|
-
print(e) # "Monthly quota exceeded"
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
## Links
|
|
157
|
-
|
|
158
|
-
- [Documentation](https://snap-render.com/docs)
|
|
159
|
-
- [Pricing](https://snap-render.com/pricing)
|
|
160
|
-
- [Dashboard](https://app.snap-render.com/dashboard)
|
|
161
|
-
- [Blog](https://snap-render.com/blog)
|
|
162
|
-
- [GitHub](https://github.com/User0856/snaprender)
|
|
163
|
-
|
|
164
|
-
## License
|
|
165
|
-
|
|
166
|
-
MIT
|
snaprender-0.7.0/README.md
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
# snaprender
|
|
2
|
-
|
|
3
|
-
[](https://pypi.org/project/snaprender/)
|
|
4
|
-
[](https://pypi.org/project/snaprender/)
|
|
5
|
-
[](https://github.com/User0856/snaprender/blob/main/LICENSE)
|
|
6
|
-
|
|
7
|
-
Official Python SDK for the [SnapRender](https://snap-render.com) Screenshot API — capture pixel-perfect screenshots of any webpage with a single API call.
|
|
8
|
-
|
|
9
|
-
**500 free screenshots/month — [Get your API key](https://snap-render.com/auth/signup?ref=pypi)**
|
|
10
|
-
|
|
11
|
-
## Features
|
|
12
|
-
|
|
13
|
-
- **Multiple output formats** — PNG, JPEG, WebP, and PDF
|
|
14
|
-
- **Device emulation** — iPhone, iPad, Pixel, and more presets
|
|
15
|
-
- **Dark mode** — Capture pages with `prefers-color-scheme: dark`
|
|
16
|
-
- **Ad blocking** — Remove ads automatically before capture
|
|
17
|
-
- **Cookie banner removal** — Clean screenshots without consent popups
|
|
18
|
-
- **Smart caching** — Configurable CDN cache with TTL control
|
|
19
|
-
- **Full-page captures** — Screenshot the entire scrollable page
|
|
20
|
-
|
|
21
|
-
## Install
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
pip install snaprender
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Quick Start
|
|
28
|
-
|
|
29
|
-
```python
|
|
30
|
-
from snaprender import SnapRender
|
|
31
|
-
|
|
32
|
-
snap = SnapRender(api_key="sk_live_...")
|
|
33
|
-
|
|
34
|
-
# Capture a screenshot
|
|
35
|
-
image = snap.capture("https://example.com")
|
|
36
|
-
with open("screenshot.png", "wb") as f:
|
|
37
|
-
f.write(image)
|
|
38
|
-
|
|
39
|
-
# Capture with options
|
|
40
|
-
jpg = snap.capture(
|
|
41
|
-
"https://example.com",
|
|
42
|
-
format="jpeg",
|
|
43
|
-
width=1920,
|
|
44
|
-
height=1080,
|
|
45
|
-
full_page=True,
|
|
46
|
-
dark_mode=True,
|
|
47
|
-
quality=95,
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
# Check cache status
|
|
51
|
-
info = snap.info("https://example.com")
|
|
52
|
-
print(info["cached"]) # True/False
|
|
53
|
-
print(info["expires_at"]) # ISO date string (when cached)
|
|
54
|
-
|
|
55
|
-
# Get usage
|
|
56
|
-
usage = snap.usage()
|
|
57
|
-
print(f"{usage['used']}/{usage['limit']} screenshots used")
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## Context Manager
|
|
61
|
-
|
|
62
|
-
```python
|
|
63
|
-
with SnapRender(api_key="sk_live_...") as snap:
|
|
64
|
-
image = snap.capture("https://example.com")
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## API
|
|
68
|
-
|
|
69
|
-
### `SnapRender(api_key, base_url="https://app.snap-render.com", timeout=60.0)`
|
|
70
|
-
|
|
71
|
-
### `snap.capture(url, **options)` -> `bytes`
|
|
72
|
-
|
|
73
|
-
| Option | Type | Default | Description |
|
|
74
|
-
|--------|------|---------|-------------|
|
|
75
|
-
| `url` | str | — | URL to capture (required) |
|
|
76
|
-
| `format` | str | `"png"` | `"png"`, `"jpeg"`, `"webp"`, or `"pdf"` |
|
|
77
|
-
| `width` | int | `1280` | Viewport width |
|
|
78
|
-
| `height` | int | `800` | Viewport height |
|
|
79
|
-
| `full_page` | bool | `False` | Capture full scrollable page |
|
|
80
|
-
| `quality` | int | `90` | JPEG/WebP quality (1-100) |
|
|
81
|
-
| `delay` | int | `0` | Wait ms after page load |
|
|
82
|
-
| `dark_mode` | bool | `False` | Emulate dark mode |
|
|
83
|
-
| `block_ads` | bool | `True` | Block ad networks |
|
|
84
|
-
| `block_cookie_banners` | bool | `True` | Remove cookie banners |
|
|
85
|
-
| `device` | str | — | Device preset (`"iphone_14"`, `"iphone_15_pro"`, `"pixel_7"`, `"ipad_pro"`, `"macbook_pro"`) |
|
|
86
|
-
| `hide_selectors` | str | — | Comma-separated CSS selectors to hide |
|
|
87
|
-
| `click_selector` | str | — | CSS selector to click before capture |
|
|
88
|
-
| `user_agent` | str | — | Custom user agent string |
|
|
89
|
-
| `cache` | bool | `True` | Use cache |
|
|
90
|
-
| `cache_ttl` | int | `86400` | Cache TTL in seconds |
|
|
91
|
-
| `response_type` | str | — | `"json"` returns metadata + base64 data URI |
|
|
92
|
-
|
|
93
|
-
### `snap.info(url)` -> `dict`
|
|
94
|
-
|
|
95
|
-
Check if a screenshot is cached without capturing. Returns `{ url, cached, cache_key, cached_at, expires_at, content_type }`.
|
|
96
|
-
|
|
97
|
-
### `snap.usage()` -> `dict`
|
|
98
|
-
|
|
99
|
-
Get current month's usage.
|
|
100
|
-
|
|
101
|
-
### `snap.usage_daily(days=30)` -> `dict`
|
|
102
|
-
|
|
103
|
-
Get daily usage breakdown (default 30 days).
|
|
104
|
-
|
|
105
|
-
## LangChain Integration
|
|
106
|
-
|
|
107
|
-
Use SnapRender as a [LangChain](https://python.langchain.com/) tool for AI agents:
|
|
108
|
-
|
|
109
|
-
```python
|
|
110
|
-
import os
|
|
111
|
-
from langchain_core.tools import tool
|
|
112
|
-
from snaprender import SnapRender
|
|
113
|
-
|
|
114
|
-
client = SnapRender(api_key=os.environ["SNAPRENDER_API_KEY"])
|
|
115
|
-
|
|
116
|
-
@tool
|
|
117
|
-
def take_screenshot(url: str, format: str = "png", dark_mode: bool = False) -> str:
|
|
118
|
-
"""Capture a screenshot of any website URL. Returns base64 data URI."""
|
|
119
|
-
result = client.capture(
|
|
120
|
-
url, format=format, dark_mode=dark_mode, response_type="json"
|
|
121
|
-
)
|
|
122
|
-
return result["image"]
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
Works with LangGraph, CrewAI, and any framework that supports LangChain tools.
|
|
126
|
-
|
|
127
|
-
## Error Handling
|
|
128
|
-
|
|
129
|
-
```python
|
|
130
|
-
from snaprender import SnapRender, SnapRenderError
|
|
131
|
-
|
|
132
|
-
snap = SnapRender(api_key="sk_live_...")
|
|
133
|
-
|
|
134
|
-
try:
|
|
135
|
-
snap.capture("https://example.com")
|
|
136
|
-
except SnapRenderError as e:
|
|
137
|
-
print(e.code) # "QUOTA_EXCEEDED"
|
|
138
|
-
print(e.status) # 429
|
|
139
|
-
print(e) # "Monthly quota exceeded"
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## Links
|
|
143
|
-
|
|
144
|
-
- [Documentation](https://snap-render.com/docs)
|
|
145
|
-
- [Pricing](https://snap-render.com/pricing)
|
|
146
|
-
- [Dashboard](https://app.snap-render.com/dashboard)
|
|
147
|
-
- [Blog](https://snap-render.com/blog)
|
|
148
|
-
- [GitHub](https://github.com/User0856/snaprender)
|
|
149
|
-
|
|
150
|
-
## License
|
|
151
|
-
|
|
152
|
-
MIT
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|