snaprender 0.1.0__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,14 @@
1
+ node_modules/
2
+ dist/
3
+ .env
4
+ .env.local
5
+ .env.production
6
+ *.log
7
+ coverage/
8
+ .vscode/
9
+ .idea/
10
+ *.tsbuildinfo
11
+ .DS_Store
12
+ tmp/
13
+ site/.astro/
14
+ site/dist/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SnapRender
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.4
2
+ Name: snaprender
3
+ Version: 0.1.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-Expression: 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
+ Official Python SDK for the [SnapRender](https://snap-render.com) Screenshot API.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ pip install snaprender
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```python
28
+ from snaprender import SnapRender
29
+
30
+ snap = SnapRender(api_key="sk_live_...")
31
+
32
+ # Capture a screenshot
33
+ image = snap.capture("https://example.com")
34
+ with open("screenshot.png", "wb") as f:
35
+ f.write(image)
36
+
37
+ # Capture with options
38
+ jpg = snap.capture(
39
+ "https://example.com",
40
+ format="jpeg",
41
+ width=1920,
42
+ height=1080,
43
+ full_page=True,
44
+ dark_mode=True,
45
+ quality=95,
46
+ )
47
+
48
+ # Check cache status
49
+ info = snap.info("https://example.com")
50
+ print(info["cached"]) # True/False
51
+
52
+ # Get usage
53
+ usage = snap.usage()
54
+ print(f"{usage['used']}/{usage['limit']} screenshots used")
55
+ ```
56
+
57
+ ## Context Manager
58
+
59
+ ```python
60
+ with SnapRender(api_key="sk_live_...") as snap:
61
+ image = snap.capture("https://example.com")
62
+ ```
63
+
64
+ ## Error Handling
65
+
66
+ ```python
67
+ from snaprender import SnapRender, SnapRenderError
68
+
69
+ snap = SnapRender(api_key="sk_live_...")
70
+
71
+ try:
72
+ snap.capture("https://example.com")
73
+ except SnapRenderError as e:
74
+ print(e.code) # "QUOTA_EXCEEDED"
75
+ print(e.status) # 429
76
+ print(e) # "Monthly quota exceeded"
77
+ ```
78
+
79
+ ## API
80
+
81
+ ### `SnapRender(api_key, base_url="https://app.snap-render.com", timeout=60.0)`
82
+
83
+ ### `snap.capture(url, **options)` -> `bytes`
84
+
85
+ | Option | Type | Default | Description |
86
+ |--------|------|---------|-------------|
87
+ | `url` | str | — | URL to capture (required) |
88
+ | `format` | str | `"png"` | `"png"`, `"jpeg"`, `"webp"`, or `"pdf"` |
89
+ | `width` | int | `1280` | Viewport width |
90
+ | `height` | int | `800` | Viewport height |
91
+ | `full_page` | bool | `False` | Capture full scrollable page |
92
+ | `quality` | int | `90` | JPEG/WebP quality (1-100) |
93
+ | `delay` | int | `0` | Wait ms after page load |
94
+ | `dark_mode` | bool | `False` | Emulate dark mode |
95
+ | `block_ads` | bool | `True` | Block ad networks |
96
+ | `block_cookie_banners` | bool | `True` | Remove cookie banners |
97
+ | `device` | str | — | Device preset (e.g. `"iPhone 15"`) |
98
+ | `cache` | bool | `True` | Use cache |
99
+ | `cache_ttl` | int | `86400` | Cache TTL in seconds |
100
+
101
+ ### `snap.info(url)` -> `dict`
102
+ ### `snap.usage()` -> `dict`
103
+ ### `snap.usage_daily(days=30)` -> `dict`
@@ -0,0 +1,89 @@
1
+ # snaprender
2
+
3
+ Official Python SDK for the [SnapRender](https://snap-render.com) Screenshot API.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install snaprender
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from snaprender import SnapRender
15
+
16
+ snap = SnapRender(api_key="sk_live_...")
17
+
18
+ # Capture a screenshot
19
+ image = snap.capture("https://example.com")
20
+ with open("screenshot.png", "wb") as f:
21
+ f.write(image)
22
+
23
+ # Capture with options
24
+ jpg = snap.capture(
25
+ "https://example.com",
26
+ format="jpeg",
27
+ width=1920,
28
+ height=1080,
29
+ full_page=True,
30
+ dark_mode=True,
31
+ quality=95,
32
+ )
33
+
34
+ # Check cache status
35
+ info = snap.info("https://example.com")
36
+ print(info["cached"]) # True/False
37
+
38
+ # Get usage
39
+ usage = snap.usage()
40
+ print(f"{usage['used']}/{usage['limit']} screenshots used")
41
+ ```
42
+
43
+ ## Context Manager
44
+
45
+ ```python
46
+ with SnapRender(api_key="sk_live_...") as snap:
47
+ image = snap.capture("https://example.com")
48
+ ```
49
+
50
+ ## Error Handling
51
+
52
+ ```python
53
+ from snaprender import SnapRender, SnapRenderError
54
+
55
+ snap = SnapRender(api_key="sk_live_...")
56
+
57
+ try:
58
+ snap.capture("https://example.com")
59
+ except SnapRenderError as e:
60
+ print(e.code) # "QUOTA_EXCEEDED"
61
+ print(e.status) # 429
62
+ print(e) # "Monthly quota exceeded"
63
+ ```
64
+
65
+ ## API
66
+
67
+ ### `SnapRender(api_key, base_url="https://app.snap-render.com", timeout=60.0)`
68
+
69
+ ### `snap.capture(url, **options)` -> `bytes`
70
+
71
+ | Option | Type | Default | Description |
72
+ |--------|------|---------|-------------|
73
+ | `url` | str | — | URL to capture (required) |
74
+ | `format` | str | `"png"` | `"png"`, `"jpeg"`, `"webp"`, or `"pdf"` |
75
+ | `width` | int | `1280` | Viewport width |
76
+ | `height` | int | `800` | Viewport height |
77
+ | `full_page` | bool | `False` | Capture full scrollable page |
78
+ | `quality` | int | `90` | JPEG/WebP quality (1-100) |
79
+ | `delay` | int | `0` | Wait ms after page load |
80
+ | `dark_mode` | bool | `False` | Emulate dark mode |
81
+ | `block_ads` | bool | `True` | Block ad networks |
82
+ | `block_cookie_banners` | bool | `True` | Remove cookie banners |
83
+ | `device` | str | — | Device preset (e.g. `"iPhone 15"`) |
84
+ | `cache` | bool | `True` | Use cache |
85
+ | `cache_ttl` | int | `86400` | Cache TTL in seconds |
86
+
87
+ ### `snap.info(url)` -> `dict`
88
+ ### `snap.usage()` -> `dict`
89
+ ### `snap.usage_daily(days=30)` -> `dict`
@@ -0,0 +1,18 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "snaprender"
7
+ version = "0.1.0"
8
+ description = "Official Python SDK for SnapRender Screenshot API"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.8"
12
+ dependencies = ["httpx>=0.24"]
13
+ keywords = ["screenshot", "api", "snaprender", "capture", "webpage"]
14
+
15
+ [project.urls]
16
+ Homepage = "https://snap-render.com"
17
+ Documentation = "https://snap-render.com/docs"
18
+ Repository = "https://github.com/User0856/snaprender"
@@ -0,0 +1,6 @@
1
+ """Official Python SDK for SnapRender Screenshot API."""
2
+
3
+ from .client import SnapRender, SnapRenderError
4
+
5
+ __all__ = ["SnapRender", "SnapRenderError"]
6
+ __version__ = "0.1.0"
@@ -0,0 +1,144 @@
1
+ """SnapRender API client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Optional
6
+
7
+ import httpx
8
+
9
+
10
+ class SnapRenderError(Exception):
11
+ """Error returned by the SnapRender API."""
12
+
13
+ def __init__(self, message: str, code: str = "UNKNOWN", status: int = 0):
14
+ super().__init__(message)
15
+ self.code = code
16
+ self.status = status
17
+
18
+
19
+ class SnapRender:
20
+ """SnapRender Screenshot API client.
21
+
22
+ Usage::
23
+
24
+ snap = SnapRender(api_key="sk_live_...")
25
+ image = snap.capture("https://example.com")
26
+ with open("screenshot.png", "wb") as f:
27
+ f.write(image)
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ api_key: str,
33
+ base_url: str = "https://app.snap-render.com",
34
+ timeout: float = 60.0,
35
+ ):
36
+ if not api_key:
37
+ raise ValueError("api_key is required")
38
+ self._base_url = base_url.rstrip("/")
39
+ self._client = httpx.Client(
40
+ base_url=self._base_url,
41
+ headers={"X-API-Key": api_key},
42
+ timeout=timeout,
43
+ )
44
+
45
+ def capture(
46
+ self,
47
+ url: str,
48
+ *,
49
+ format: str = "png",
50
+ width: Optional[int] = None,
51
+ height: Optional[int] = None,
52
+ full_page: Optional[bool] = None,
53
+ quality: Optional[int] = None,
54
+ delay: Optional[int] = None,
55
+ dark_mode: Optional[bool] = None,
56
+ block_ads: Optional[bool] = None,
57
+ block_cookie_banners: Optional[bool] = None,
58
+ hide_selectors: Optional[str] = None,
59
+ click_selector: Optional[str] = None,
60
+ device: Optional[str] = None,
61
+ user_agent: Optional[str] = None,
62
+ cache: Optional[bool] = None,
63
+ cache_ttl: Optional[int] = None,
64
+ ) -> bytes:
65
+ """Capture a screenshot and return the image bytes."""
66
+ params: dict[str, Any] = {"url": url, "format": format}
67
+ if width is not None:
68
+ params["width"] = width
69
+ if height is not None:
70
+ params["height"] = height
71
+ if full_page is not None:
72
+ params["full_page"] = str(full_page).lower()
73
+ if quality is not None:
74
+ params["quality"] = quality
75
+ if delay is not None:
76
+ params["delay"] = delay
77
+ if dark_mode is not None:
78
+ params["dark_mode"] = str(dark_mode).lower()
79
+ if block_ads is not None:
80
+ params["block_ads"] = str(block_ads).lower()
81
+ if block_cookie_banners is not None:
82
+ params["block_cookie_banners"] = str(block_cookie_banners).lower()
83
+ if hide_selectors is not None:
84
+ params["hide_selectors"] = hide_selectors
85
+ if click_selector is not None:
86
+ params["click_selector"] = click_selector
87
+ if device is not None:
88
+ params["device"] = device
89
+ if user_agent is not None:
90
+ params["user_agent"] = user_agent
91
+ if cache is not None:
92
+ params["cache"] = str(cache).lower()
93
+ if cache_ttl is not None:
94
+ params["cache_ttl"] = cache_ttl
95
+
96
+ resp = self._client.get("/v1/screenshot", params=params)
97
+ if resp.status_code != 200:
98
+ self._raise_error(resp)
99
+ return resp.content
100
+
101
+ def info(self, url: str, **kwargs: Any) -> dict[str, Any]:
102
+ """Get cache info for a URL without capturing."""
103
+ params: dict[str, Any] = {"url": url, **kwargs}
104
+ resp = self._client.get("/v1/screenshot/info", params=params)
105
+ if resp.status_code != 200:
106
+ self._raise_error(resp)
107
+ return resp.json()
108
+
109
+ def usage(self) -> dict[str, Any]:
110
+ """Get current month's usage."""
111
+ resp = self._client.get("/v1/usage")
112
+ if resp.status_code != 200:
113
+ self._raise_error(resp)
114
+ return resp.json()
115
+
116
+ def usage_daily(self, days: int = 30) -> dict[str, Any]:
117
+ """Get daily usage breakdown."""
118
+ resp = self._client.get("/v1/usage/daily", params={"days": days})
119
+ if resp.status_code != 200:
120
+ self._raise_error(resp)
121
+ return resp.json()
122
+
123
+ def close(self) -> None:
124
+ """Close the HTTP client."""
125
+ self._client.close()
126
+
127
+ def __enter__(self) -> SnapRender:
128
+ return self
129
+
130
+ def __exit__(self, *args: Any) -> None:
131
+ self.close()
132
+
133
+ @staticmethod
134
+ def _raise_error(resp: httpx.Response) -> None:
135
+ try:
136
+ body = resp.json()
137
+ err = body.get("error", {})
138
+ raise SnapRenderError(
139
+ err.get("message", f"HTTP {resp.status_code}"),
140
+ err.get("code", "UNKNOWN"),
141
+ resp.status_code,
142
+ )
143
+ except (ValueError, KeyError):
144
+ raise SnapRenderError(f"HTTP {resp.status_code}", "UNKNOWN", resp.status_code)