snaprender 0.6.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.
@@ -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
+ [![PyPI version](https://img.shields.io/pypi/v/snaprender)](https://pypi.org/project/snaprender/)
18
+ [![PyPI downloads](https://img.shields.io/pypi/dm/snaprender)](https://pypi.org/project/snaprender/)
19
+ [![license](https://img.shields.io/pypi/l/snaprender)](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
+ [![PyPI version](https://img.shields.io/pypi/v/snaprender)](https://pypi.org/project/snaprender/)
4
+ [![PyPI downloads](https://img.shields.io/pypi/dm/snaprender)](https://pypi.org/project/snaprender/)
5
+ [![license](https://img.shields.io/pypi/l/snaprender)](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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "snaprender"
7
- version = "0.6.0"
7
+ version = "0.7.1"
8
8
  description = "Official Python SDK for SnapRender Screenshot API"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -321,6 +321,55 @@ class SnapRender:
321
321
  self._raise_error(resp)
322
322
  return resp.json()
323
323
 
324
+ def create_webhook(
325
+ self,
326
+ url: str,
327
+ events: list[str],
328
+ ) -> dict[str, Any]:
329
+ """Create a webhook for event notifications.
330
+
331
+ Max 5 webhooks per account.
332
+ Events: screenshot.completed, quota.warning, quota.exceeded.
333
+ Returns the webhook with its signing secret.
334
+ """
335
+ body: dict[str, Any] = {"url": url, "events": events}
336
+ resp = self._client.post("/v1/webhooks", json=body)
337
+ if resp.status_code not in (200, 201):
338
+ self._raise_error(resp)
339
+ return resp.json()
340
+
341
+ def list_webhooks(self) -> list[dict[str, Any]]:
342
+ """List all webhooks for the authenticated account."""
343
+ resp = self._client.get("/v1/webhooks")
344
+ if resp.status_code != 200:
345
+ self._raise_error(resp)
346
+ return resp.json()
347
+
348
+ def delete_webhook(self, webhook_id: str) -> None:
349
+ """Delete a webhook by ID."""
350
+ resp = self._client.delete(f"/v1/webhooks/{webhook_id}")
351
+ if resp.status_code not in (200, 204):
352
+ self._raise_error(resp)
353
+
354
+ def test_webhook(self, webhook_id: str) -> dict[str, Any]:
355
+ """Send a test payload to a webhook."""
356
+ resp = self._client.post(f"/v1/webhooks/{webhook_id}/test")
357
+ if resp.status_code != 200:
358
+ self._raise_error(resp)
359
+ return resp.json()
360
+
361
+ @staticmethod
362
+ def verify_webhook_signature(payload: str, signature: str, secret: str) -> bool:
363
+ """Verify a webhook signature.
364
+
365
+ Use in your webhook handler to confirm the payload was sent by SnapRender.
366
+ """
367
+ import hmac
368
+ import hashlib
369
+ expected = hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()
370
+ sig = signature.removeprefix("sha256=")
371
+ return hmac.compare_digest(sig, expected)
372
+
324
373
  def info(self, url: str, **kwargs: Any) -> dict[str, Any]:
325
374
  """Get cache info for a URL without capturing.
326
375
 
snaprender-0.6.0/PKG-INFO DELETED
@@ -1,166 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: snaprender
3
- Version: 0.6.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
- [![PyPI version](https://img.shields.io/pypi/v/snaprender)](https://pypi.org/project/snaprender/)
18
- [![PyPI downloads](https://img.shields.io/pypi/dm/snaprender)](https://pypi.org/project/snaprender/)
19
- [![license](https://img.shields.io/pypi/l/snaprender)](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
@@ -1,152 +0,0 @@
1
- # snaprender
2
-
3
- [![PyPI version](https://img.shields.io/pypi/v/snaprender)](https://pypi.org/project/snaprender/)
4
- [![PyPI downloads](https://img.shields.io/pypi/dm/snaprender)](https://pypi.org/project/snaprender/)
5
- [![license](https://img.shields.io/pypi/l/snaprender)](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