peep-sdk 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,6 @@
1
+ __pycache__/
2
+ *.pyc
3
+ dist/
4
+ build/
5
+ *.egg-info/
6
+ .venv/
peep_sdk-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ShowNoMore
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,88 @@
1
+ Metadata-Version: 2.4
2
+ Name: peep-sdk
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for the Peep web scraping API — scrape, crawl, map, search, extract, and agent.
5
+ Project-URL: Homepage, https://peep.shownomore.com
6
+ Project-URL: Documentation, https://peep.shownomore.com/docs
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Keywords: api,crawler,peep,scraper,sdk,web-scraping
10
+ Requires-Python: >=3.8
11
+ Requires-Dist: requests>=2.25
12
+ Description-Content-Type: text/markdown
13
+
14
+ # peep-sdk (Python)
15
+
16
+ Official Python SDK for the [Peep](https://peep.shownomore.com) web scraping
17
+ API. Scrape, crawl, map, search, extract, and run autonomous agents. Every call
18
+ draws from your shared **Peep Card** credit balance.
19
+
20
+ ## Install
21
+
22
+ From PyPI with pip, Poetry, or uv:
23
+
24
+ ```bash
25
+ pip install peep-sdk
26
+ poetry add peep-sdk
27
+ uv add peep-sdk
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ```python
33
+ import os
34
+ from peep import Peep
35
+
36
+ peep = Peep(api_key=os.environ["PEEP_API_KEY"])
37
+
38
+ # Scrape a page to markdown
39
+ res = peep.scrape("https://example.com", formats=["markdown"])
40
+ print(res["data"]["markdown"])
41
+ print(peep.last_credits) # {"used": 1, "remaining": 499}
42
+
43
+ # Crawl a site and wait for completion
44
+ crawl = peep.crawl_and_wait("https://example.com", limit=50)
45
+
46
+ # Search the web
47
+ hits = peep.search("best web scraping api", limit=10)
48
+
49
+ # AI structured extraction
50
+ data = peep.extract_and_wait(
51
+ ["https://example.com"],
52
+ prompt="Extract the company name and pricing tiers.",
53
+ )
54
+
55
+ # Autonomous harvesting
56
+ leads = peep.agent_and_wait(
57
+ "Find flats for rent in Vivek Vihar with phone numbers and photos",
58
+ maxSources=10,
59
+ )
60
+
61
+ # Check your balance
62
+ print(peep.credits()["balance"])
63
+ ```
64
+
65
+ > Option keyword arguments are passed through verbatim — use the API's
66
+ > camelCase names, e.g. `peep.scrape(url, onlyMainContent=True)`.
67
+
68
+ ## Methods
69
+
70
+ | Method | Endpoint | Notes |
71
+ |--------|----------|-------|
72
+ | `scrape(url, **options)` | `POST /scrape` | Sync. Pass a YouTube URL for video intelligence. |
73
+ | `youtube(url)` | `POST /scrape` | Sugar for YouTube videos. |
74
+ | `map(url, **options)` | `POST /map` | Sync URL discovery. |
75
+ | `search(query, **options)` | `POST /search` | Sync. |
76
+ | `crawl(...)` / `crawl_and_wait(...)` | `POST /crawl` | Async; `*_and_wait` polls to completion. |
77
+ | `batch_scrape(...)` / `batch_scrape_and_wait(...)` | `POST /batch/scrape` | Async. |
78
+ | `extract(...)` / `extract_and_wait(...)` | `POST /extract` | Async. |
79
+ | `agent(...)` / `agent_and_wait(...)` | `POST /agent` | Async. |
80
+ | `credits()` | `GET /credits` | Free. Balance + ledger. |
81
+
82
+ Non-2xx responses raise `PeepError` with `.status` and `.code`. After every
83
+ call, `peep.last_credits` holds `{"used", "remaining"}` from the response
84
+ headers.
85
+
86
+ ## License
87
+
88
+ MIT
@@ -0,0 +1,75 @@
1
+ # peep-sdk (Python)
2
+
3
+ Official Python SDK for the [Peep](https://peep.shownomore.com) web scraping
4
+ API. Scrape, crawl, map, search, extract, and run autonomous agents. Every call
5
+ draws from your shared **Peep Card** credit balance.
6
+
7
+ ## Install
8
+
9
+ From PyPI with pip, Poetry, or uv:
10
+
11
+ ```bash
12
+ pip install peep-sdk
13
+ poetry add peep-sdk
14
+ uv add peep-sdk
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```python
20
+ import os
21
+ from peep import Peep
22
+
23
+ peep = Peep(api_key=os.environ["PEEP_API_KEY"])
24
+
25
+ # Scrape a page to markdown
26
+ res = peep.scrape("https://example.com", formats=["markdown"])
27
+ print(res["data"]["markdown"])
28
+ print(peep.last_credits) # {"used": 1, "remaining": 499}
29
+
30
+ # Crawl a site and wait for completion
31
+ crawl = peep.crawl_and_wait("https://example.com", limit=50)
32
+
33
+ # Search the web
34
+ hits = peep.search("best web scraping api", limit=10)
35
+
36
+ # AI structured extraction
37
+ data = peep.extract_and_wait(
38
+ ["https://example.com"],
39
+ prompt="Extract the company name and pricing tiers.",
40
+ )
41
+
42
+ # Autonomous harvesting
43
+ leads = peep.agent_and_wait(
44
+ "Find flats for rent in Vivek Vihar with phone numbers and photos",
45
+ maxSources=10,
46
+ )
47
+
48
+ # Check your balance
49
+ print(peep.credits()["balance"])
50
+ ```
51
+
52
+ > Option keyword arguments are passed through verbatim — use the API's
53
+ > camelCase names, e.g. `peep.scrape(url, onlyMainContent=True)`.
54
+
55
+ ## Methods
56
+
57
+ | Method | Endpoint | Notes |
58
+ |--------|----------|-------|
59
+ | `scrape(url, **options)` | `POST /scrape` | Sync. Pass a YouTube URL for video intelligence. |
60
+ | `youtube(url)` | `POST /scrape` | Sugar for YouTube videos. |
61
+ | `map(url, **options)` | `POST /map` | Sync URL discovery. |
62
+ | `search(query, **options)` | `POST /search` | Sync. |
63
+ | `crawl(...)` / `crawl_and_wait(...)` | `POST /crawl` | Async; `*_and_wait` polls to completion. |
64
+ | `batch_scrape(...)` / `batch_scrape_and_wait(...)` | `POST /batch/scrape` | Async. |
65
+ | `extract(...)` / `extract_and_wait(...)` | `POST /extract` | Async. |
66
+ | `agent(...)` / `agent_and_wait(...)` | `POST /agent` | Async. |
67
+ | `credits()` | `GET /credits` | Free. Balance + ledger. |
68
+
69
+ Non-2xx responses raise `PeepError` with `.status` and `.code`. After every
70
+ call, `peep.last_credits` holds `{"used", "remaining"}` from the response
71
+ headers.
72
+
73
+ ## License
74
+
75
+ MIT
@@ -0,0 +1,9 @@
1
+ """Official Python SDK for the Peep web scraping API.
2
+
3
+ https://peep.shownomore.com/docs
4
+ """
5
+
6
+ from .client import Peep, PeepError
7
+
8
+ __all__ = ["Peep", "PeepError"]
9
+ __version__ = "0.1.0"
@@ -0,0 +1,188 @@
1
+ """Peep API client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from typing import Any, Callable, Dict, List, Optional
7
+
8
+ import requests
9
+
10
+ _TERMINAL = ("completed", "failed", "cancelled", "canceled", "error")
11
+
12
+
13
+ class PeepError(Exception):
14
+ """Raised on any non-2xx response from the Peep API."""
15
+
16
+ def __init__(self, status: int, code: str, message: str) -> None:
17
+ super().__init__(message)
18
+ self.status = status
19
+ self.code = code
20
+
21
+
22
+ class Peep:
23
+ """Client for the Peep web scraping API.
24
+
25
+ Args:
26
+ api_key: Your ``peep_live_*`` API key.
27
+ base_url: Override the API base URL.
28
+
29
+ Option keyword arguments are passed through verbatim, so use the API's
30
+ camelCase names — e.g. ``peep.scrape(url, onlyMainContent=True)``.
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ api_key: str,
36
+ base_url: str = "https://peep.shownomore.com",
37
+ ) -> None:
38
+ if not api_key:
39
+ raise ValueError("Peep: api_key is required.")
40
+ self.api_key = api_key
41
+ self.base_url = base_url.rstrip("/")
42
+ self.last_credits: Dict[str, Optional[int]] = {"used": None, "remaining": None}
43
+ self._session = requests.Session()
44
+
45
+ # ── Internals ───────────────────────────────────────────────
46
+ def _request(
47
+ self, method: str, path: str, body: Optional[Dict[str, Any]] = None
48
+ ) -> Dict[str, Any]:
49
+ res = self._session.request(
50
+ method,
51
+ f"{self.base_url}{path}",
52
+ headers={"Authorization": f"Bearer {self.api_key}"},
53
+ json=body,
54
+ )
55
+
56
+ used = res.headers.get("x-peep-credits-used")
57
+ remaining = res.headers.get("x-peep-credits-remaining")
58
+ self.last_credits = {
59
+ "used": int(used) if used is not None else None,
60
+ "remaining": int(remaining) if remaining is not None else None,
61
+ }
62
+
63
+ data = res.json() if res.text else {}
64
+ if not res.ok:
65
+ err = data.get("error", {}) if isinstance(data, dict) else {}
66
+ raise PeepError(
67
+ res.status_code,
68
+ err.get("code", "UNKNOWN"),
69
+ err.get("message", f"Request failed with status {res.status_code}"),
70
+ )
71
+ return data
72
+
73
+ def _start_and_wait(
74
+ self,
75
+ start: Callable[[], Dict[str, Any]],
76
+ get_status: Callable[[str], Dict[str, Any]],
77
+ poll_interval: float,
78
+ timeout: float,
79
+ ) -> Dict[str, Any]:
80
+ job = start()
81
+ job_id = job.get("jobId")
82
+ if not job_id:
83
+ return job
84
+ deadline = time.time() + timeout
85
+ last = get_status(job_id)
86
+ while time.time() < deadline:
87
+ if str(last.get("status", "")).lower() in _TERMINAL:
88
+ return last
89
+ time.sleep(poll_interval)
90
+ last = get_status(job_id)
91
+ return last # timed out — return the latest snapshot
92
+
93
+ # ── Sync endpoints ──────────────────────────────────────────
94
+ def scrape(self, url: str, **options: Any) -> Dict[str, Any]:
95
+ return self._request("POST", "/api/v1/scrape", {"url": url, **options})
96
+
97
+ def youtube(self, url: str) -> Dict[str, Any]:
98
+ return self.scrape(url, formats=["markdown"])
99
+
100
+ def map(self, url: str, **options: Any) -> Dict[str, Any]:
101
+ return self._request("POST", "/api/v1/map", {"url": url, **options})
102
+
103
+ def search(self, query: str, **options: Any) -> Dict[str, Any]:
104
+ return self._request("POST", "/api/v1/search", {"query": query, **options})
105
+
106
+ # ── Async endpoints ─────────────────────────────────────────
107
+ def crawl(self, url: str, **options: Any) -> Dict[str, Any]:
108
+ return self._request("POST", "/api/v1/crawl", {"url": url, **options})
109
+
110
+ def get_crawl(self, job_id: str) -> Dict[str, Any]:
111
+ return self._request("GET", f"/api/v1/crawl/{job_id}")
112
+
113
+ def crawl_and_wait(
114
+ self, url: str, poll_interval: float = 2.5, timeout: float = 300, **options: Any
115
+ ) -> Dict[str, Any]:
116
+ return self._start_and_wait(
117
+ lambda: self.crawl(url, **options), self.get_crawl, poll_interval, timeout
118
+ )
119
+
120
+ def batch_scrape(
121
+ self, urls: List[str], scrape_options: Optional[Dict[str, Any]] = None
122
+ ) -> Dict[str, Any]:
123
+ return self._request(
124
+ "POST",
125
+ "/api/v1/batch/scrape",
126
+ {"urls": urls, "scrapeOptions": scrape_options or {}},
127
+ )
128
+
129
+ def get_batch(self, job_id: str) -> Dict[str, Any]:
130
+ return self._request("GET", f"/api/v1/batch/scrape/{job_id}")
131
+
132
+ def batch_scrape_and_wait(
133
+ self,
134
+ urls: List[str],
135
+ scrape_options: Optional[Dict[str, Any]] = None,
136
+ poll_interval: float = 2.5,
137
+ timeout: float = 300,
138
+ ) -> Dict[str, Any]:
139
+ return self._start_and_wait(
140
+ lambda: self.batch_scrape(urls, scrape_options),
141
+ self.get_batch,
142
+ poll_interval,
143
+ timeout,
144
+ )
145
+
146
+ def extract(self, urls: List[str], **options: Any) -> Dict[str, Any]:
147
+ return self._request("POST", "/api/v1/extract", {"urls": urls, **options})
148
+
149
+ def get_extract(self, job_id: str) -> Dict[str, Any]:
150
+ return self._request("GET", f"/api/v1/extract/{job_id}")
151
+
152
+ def extract_and_wait(
153
+ self,
154
+ urls: List[str],
155
+ poll_interval: float = 2.5,
156
+ timeout: float = 300,
157
+ **options: Any,
158
+ ) -> Dict[str, Any]:
159
+ return self._start_and_wait(
160
+ lambda: self.extract(urls, **options),
161
+ self.get_extract,
162
+ poll_interval,
163
+ timeout,
164
+ )
165
+
166
+ def agent(self, prompt: str, **options: Any) -> Dict[str, Any]:
167
+ return self._request("POST", "/api/v1/agent", {"prompt": prompt, **options})
168
+
169
+ def get_agent(self, job_id: str) -> Dict[str, Any]:
170
+ return self._request("GET", f"/api/v1/agent/{job_id}")
171
+
172
+ def agent_and_wait(
173
+ self,
174
+ prompt: str,
175
+ poll_interval: float = 2.5,
176
+ timeout: float = 300,
177
+ **options: Any,
178
+ ) -> Dict[str, Any]:
179
+ return self._start_and_wait(
180
+ lambda: self.agent(prompt, **options),
181
+ self.get_agent,
182
+ poll_interval,
183
+ timeout,
184
+ )
185
+
186
+ # ── Credits ─────────────────────────────────────────────────
187
+ def credits(self) -> Dict[str, Any]:
188
+ return self._request("GET", "/api/v1/credits")
@@ -0,0 +1,20 @@
1
+ [project]
2
+ name = "peep-sdk"
3
+ version = "0.1.0"
4
+ description = "Official Python SDK for the Peep web scraping API — scrape, crawl, map, search, extract, and agent."
5
+ readme = "README.md"
6
+ requires-python = ">=3.8"
7
+ license = { text = "MIT" }
8
+ keywords = ["peep", "web-scraping", "crawler", "scraper", "api", "sdk"]
9
+ dependencies = ["requests>=2.25"]
10
+
11
+ [project.urls]
12
+ Homepage = "https://peep.shownomore.com"
13
+ Documentation = "https://peep.shownomore.com/docs"
14
+
15
+ [build-system]
16
+ requires = ["hatchling"]
17
+ build-backend = "hatchling.build"
18
+
19
+ [tool.hatch.build.targets.wheel]
20
+ packages = ["peep"]