olostep 0.9.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.
olostep-0.9.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Olostep
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.
olostep-0.9.0/PKG-INFO ADDED
@@ -0,0 +1,360 @@
1
+ Metadata-Version: 2.4
2
+ Name: olostep
3
+ Version: 0.9.0
4
+ Summary: Olostep Python SDK - Programmatic access to the web
5
+ License-File: LICENSE
6
+ Author: Olostep Team
7
+ Author-email: team@olostep.com
8
+ Requires-Python: >=3.11,<3.14
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Dist: httpx[http2] (>=0.28.1,<0.29.0)
14
+ Requires-Dist: pydantic (>=2.11.3,<3.0.0)
15
+ Description-Content-Type: text/markdown
16
+
17
+ # Olostep Python SDK (official)
18
+
19
+ Olostep provides a **programmatic layer to access and interact with the web**.
20
+ Fetch clean, ready-to-use data for your AI from any website in **1–5 seconds**, and scale up to **100K parallel requests in minutes**. You can:
21
+
22
+ - **Scrape** websites with various output formats incl. structured data.
23
+ - **Batch** scrape hundreds of websites
24
+ - **Crawl** websites
25
+ - Create massive **sitemaps** in seconds
26
+
27
+ [Get a free API key here](https://www.olostep.com/?utm_source=python_readme)
28
+
29
+ ## Python SDK vs API
30
+
31
+ The Olostep Python SDK offers features and ergonomics beyond the [REST API](https://docs.olostep.com):
32
+
33
+ It streamlines web data access with a type-safe, stateful, and ergonomic experience not available in the REST API.
34
+
35
+
36
+ - **Discoverable and predictable interface**: Dot-notation namespaces, stateful return objects (IDs, cursors), convenience methods and rich `__repr__`s.
37
+ - **Async-first, Sync-friendly**: The SDK is async by default (but also provides a sync facade) This allows you async performance over an otherwise high latency sync API.
38
+ - **Type Safety & Input Coercion**: The client uses Pydantic to validate inputs and coerce them where possible. It also provides you with full type hints. It also catches errors before the request is being sent to the API (can be disabled).
39
+ - **Elegant Pagination**: `async for x in y.(batch_size=10):` for `batch` and `crawl` and `sitemap` handles all pagination.
40
+ - **Wait Methods**: `.wait_till_done()` for `batch` and `crawl` workflows.
41
+ - **Automatic retries**, improved errors and connection handling for high latency and throughput.
42
+ - **Python 3.11+**.
43
+
44
+
45
+
46
+ ## Quick start
47
+
48
+
49
+ ```bash
50
+ $ pip install olostep
51
+ ```
52
+
53
+ ```python
54
+ """
55
+ The quickstart uses the async/await interface as it's the default and generally preferred.
56
+ * If you need a blocking interface scroll to the end of this codeblock.
57
+ * If you want to see the full interfaces scroll to the next section.
58
+ """
59
+
60
+ from olostep import OlostepClient
61
+
62
+ # Provide the API key either via passing in the 'api_key' parameter or
63
+ # by setting the OLOSTEP_API_KEY environment variable
64
+ client = OlostepClient(api_key="YOUR_REAL_KEY")
65
+
66
+
67
+
68
+ # MINIMAL SCRAPE EXAMPLE
69
+
70
+ scrape_result = await client.scrape("https://example.com")
71
+ # -> ScrapeResult(id='scrape_123', available=['html_content', 'markdown_content'])
72
+
73
+
74
+
75
+ # MINIMAL BATCH EXAMPLE
76
+
77
+ batch = await client.batch(["https://site1.com", "https://site2.com"])
78
+ # -> Batch(id='batch_123', urls=2)
79
+
80
+ # waits for all the batch jobs to finish, then starts fetching the results in batches
81
+ async for item in batch.items():
82
+ content = await item.retrieve(["html"])
83
+ print(f"{item.url}: {len(content.html_content)} bytes")
84
+
85
+
86
+
87
+ # MINIMAL CRAWL EXAMPLE
88
+
89
+ crawl = await client.crawl("https://example.com", max_pages=100)
90
+ # -> Crawl(id='crawl_123', urls=100)
91
+
92
+
93
+ async for page in crawl.pages():
94
+ content = await page.retrieve(["html"])
95
+ print(f"{page.url}: {len(content.html_content)} bytes")
96
+
97
+
98
+
99
+ # SYNC (FACADE) CLIENT
100
+ # this client is just a wrapper around the async client.
101
+ # The interface is the same, just don't use await.
102
+ # If you can use the OlostepClient instead, do so.
103
+ from olostep import SyncOlostepClient
104
+
105
+ client = SyncOlostepClient(api_key="YOUR_REAL_KEY")
106
+
107
+ scrape_result = client.scrape("https://example.com")
108
+ # -> ScrapeResult(id='scrape_123', available=['html_content', 'markdown_content'])
109
+ ```
110
+
111
+ ## API Design
112
+
113
+ The SDK provides a clean, Pythonic interface organized into logical namespaces. Each operation returns stateful objects with ergonomic methods for follow-up operations.
114
+
115
+
116
+ #### Scraping
117
+ ```python
118
+ from olostep import OlostepClient
119
+ from olostep import Country, FillInputAction, Format, LLMExtract, LinksOnPage, ScreenSize, Transformer, WaitAction
120
+
121
+ client = OlostepClient(api_key="YOUR_REAL_KEY")
122
+
123
+
124
+ # Minimal: Just scrape a URL
125
+ result = await client.scrape("https://example.com")
126
+ # ScrapeResult(id='scrape_123', available=['html_content', 'markdown_content'])
127
+
128
+ # Maximal: Full control over scraping behavior
129
+ result = await client.scrape(
130
+ "https://example.com",
131
+ wait_before_scraping=3000,
132
+ formats=[Format.HTML, Format.MARKDOWN],
133
+ remove_css_selectors=["script", ".popup"],
134
+ actions=[
135
+ WaitAction(milliseconds=1500),
136
+ FillInputAction(selector="searchbox", value="olostep")
137
+ ],
138
+ country=Country.US,
139
+ transformer=Transformer("postlight"),
140
+ remove_images=True,
141
+ remove_class_names=["ad"],
142
+ parser="VALID_PARSER", #check website for valid parsers
143
+ llm_extract=LLMExtract(schema="YOUR_SCHEMA"),
144
+ links_on_page=LinksOnPage(
145
+ absolute_links=False,
146
+ query_to_order_links_by='cars',
147
+ include_links=["/events/**", "/offers/**"],
148
+ exclude_links=[".pdf"]
149
+ ),
150
+ screen_size=ScreenSize(screen_width=1920, screen_height=1080),
151
+ metadata={"custom": "sidecart_data"} # Not supported yet
152
+ )
153
+ ```
154
+
155
+ #### Batch Processing
156
+ ```python
157
+ from olostep import OlostepClient
158
+ from olostep import BatchItem, Country
159
+
160
+ client = OlostepClient(api_key="YOUR_REAL_KEY")
161
+
162
+
163
+ # Minimal: Process a list of URLs
164
+ batch = await client.batch(["https://site1.com", "https://site2.com"])
165
+ # Batch(id='batch_123', urls=2)
166
+
167
+ # Maximal: Advanced batch with custom IDs and options
168
+ batch = await client.batch(
169
+ [
170
+ BatchItem(url="https://www.google.com/search?q=olostep"),
171
+ BatchItem(url="https://www.google.com/search?q=olostep+api", custom_id="news_2")
172
+ ],
173
+ country=Country.US,
174
+ parser_id="@olostep/google-search"
175
+ )
176
+
177
+ # This is optional but you can check on the process of your batch at any time with:
178
+ info = await batch.info()
179
+ # -> BatchInfo(id='batch_123', status='in_progress', completed=1/2, age=2h ago)
180
+
181
+ # Also optional: Wait for completion.
182
+ # Pass in `check_every_n_secs=` to change interval, default 10
183
+ await batch.wait_till_done()
184
+
185
+
186
+ # Note: batch.items() automatically checks if the batch is completed before starting to return elements (can be disabled by passing in `wait_for_completion=False`)
187
+ async for item in batch.items(batch_size=10):
188
+ content = await item.retrieve(["html", "json"]) # json from the parser
189
+ print(f"{item.custom_id}: {len(content.html_content)} bytes")
190
+
191
+ # Alternative: Direct API access (stateless)
192
+ async for item in client.batch.items(batch_id='a_batch_id', batch_size=10):
193
+ content = await item.retrieve(["html", "json"])
194
+ print(f"{item.custom_id}: {len(content.html_content)} bytes")
195
+ ```
196
+
197
+ #### Web Crawling
198
+ ```python
199
+ # Minimal: Crawl a site with default settings
200
+ crawl = await client.crawl("https://example.com", max_pages=100)
201
+ # Crawl(id='crawl_123', urls=100)
202
+
203
+ # Maximal: Advanced crawling with filters and limits
204
+ crawl = await client.crawl(
205
+ "https://example.com",
206
+ max_pages=1000,
207
+ max_depth=3,
208
+ include_urls=["/articles/**", "/news/**"],
209
+ exclude_urls=["/ads/**", "/tracking/**"],
210
+ include_external=False,
211
+ include_subdomain=True,
212
+ search_query="hot shingles",
213
+ top_n=50
214
+ )
215
+
216
+ # This is optional but you can check on the process of your crawl at any time with:
217
+ info = await crawl.info() # CrawlInfo(id='crawl_123', status='in_progress', pages_count=42, age=15m ago)
218
+
219
+ # Also optional: Wait for completion.
220
+ # Pass in `check_every_n_secs=` to change interval, default 10
221
+ await crawl.wait_till_done()
222
+
223
+ # Note: crawl.pages automatically checks if the batch is completed before starting to return elements (can be disabled by passing in `wait_for_completion=False`)
224
+ async for page in crawl.pages():
225
+ content = await page.retrieve(["html"])
226
+ print(f"{page.url}: {len(content.html_content)} bytes")
227
+
228
+ # Alternative: Direct API access (stateless)
229
+ async for page in client.crawl.pages(crawl_id='a_crawl_id'):
230
+ content = await page.retrieve(["html"])
231
+ print(f"{page.url}: {len(content.html_content)} bytes")
232
+
233
+ ```
234
+
235
+ #### Site Mapping
236
+ ```python
237
+ # Minimal: Extract all links from a site
238
+ sitemap = await client.sitemap("https://example.com")
239
+ # Sitemap(id='map_123', urls_count=150, has_more=True)
240
+
241
+ # Maximal: Advanced link extraction with filters
242
+ sitemap = await client.sitemap(
243
+ "https://example.com",
244
+ search_query="documentation",
245
+ top_n=500,
246
+ include_subdomain=True,
247
+ include_urls=["/docs/**", "/api/**"],
248
+ exclude_urls=["/admin/**", "/private/**"]
249
+ )
250
+
251
+ # Seamless iteration over all URLs (auto-pagination)
252
+ all_urls = []
253
+ async for url in sitemap.urls(): # async generator
254
+ print(f"Found URL: {url}")
255
+ all_urls.append(url)
256
+ # Note: This can yield tens of thousands of URLs. If you can don't
257
+ # create a list but use the generator as such.
258
+
259
+
260
+ ```
261
+
262
+ #### Data Retrieval
263
+ ```python
264
+ # Notes:
265
+ # * You should generally not need to use this endpoint as the other endpoints generate stateful return objects that can retrieve content.
266
+ # * Not all formats are available all the time
267
+
268
+ # Minimal: Get content by retrieve ID
269
+ result = await client.retrieve("ret_123")
270
+ # ScrapeResult(id='ret_123', available=[...])
271
+
272
+ # Maximal: Get multiple formats
273
+ result = await client.retrieve("ret_123", ["html", "markdown", "text", "json"])
274
+ # ScrapeResult(id='ret_123', available=['html_content', 'markdown_content', 'text_content', 'json_content'])
275
+
276
+
277
+
278
+ ```
279
+
280
+
281
+ ### Method Shorthands
282
+
283
+ The SDK provides convenient shorthand methods for common operations:
284
+
285
+ ```python
286
+ # These are equivalent:
287
+ await client.scrape("https://example.com") # shorthand
288
+ await client.scrape.create("https://example.com") # explicit method
289
+
290
+ await client.batch(["url1", "url2"]) # shorthand
291
+ await client.batch.start(["url1", "url2"]) # explicit method
292
+
293
+ await client.crawl("https://example.com") # shorthand
294
+ await client.crawl.start("https://example.com") # explicit method
295
+
296
+ await client.sitemap("https://example.com") # shorthand
297
+ await client.sitemap.create("https://example.com") # explicit method
298
+
299
+ await client.retrieve("ret_123") # shorthand
300
+ await client.retrieve.get("ret_123") # explicit method
301
+ ```
302
+
303
+ ### Smart Input Coercion
304
+
305
+ The SDK intelligently handles various input formats for maximum convenience:
306
+
307
+ ```python
308
+ # Formats: string, list, or enum
309
+ await client.scrape("https://example.com", formats="html")
310
+ await client.scrape("https://example.com", formats=["html", "markdown"])
311
+
312
+
313
+ # Countries: case-insensitive strings or enums
314
+ await client.scrape("https://example.com", country="us")
315
+ await client.scrape("https://example.com", country=Country.US)
316
+
317
+ # Lists: single values or lists
318
+ await client.batch("https://example.com") # Single URL
319
+ await client.batch(["https://a.com", "https://b.com"]) # Multiple URLs
320
+ ```
321
+
322
+
323
+
324
+ ### Logging & Error Handling
325
+
326
+ The SDK root logger is predictably called `olostep`. Granular sub-loggers exist.
327
+ The recommended log level is `INFO` or higher.
328
+ `DEBUG` really means debug and is very detailed.
329
+
330
+
331
+ Handling the error behavior of the API can quite complex. The SDK handles error detection for you and returns the following errors:
332
+
333
+ Our exception hierarchy:
334
+ ```
335
+ * Olostep_BaseError -------------------------------------- <- Catch base class for all errors
336
+ x Olostep_APIConnectionError --------------------------- <- No connection to the API
337
+ x OlostepServerError_BaseError ------------------------- <- Server-issued errors (still detected in client ofc)
338
+ + OlostepServerError_TemporaryIssue
339
+ - OlostepServerError_NetworkBusy
340
+ - OlostepServerError_InternalNetworkIssue
341
+ + OlostepServerError_RequestUnprocessable
342
+ - OlostepServerError_ParserNotFound
343
+ - OlostepServerError_OutOfResources
344
+ + OlostepServerError_BlacklistedDomain
345
+ + OlostepServerError_FeatureApprovalRequired
346
+ + OlostepServerError_AuthFailed
347
+ + OlostepServerError_CreditsExhausted
348
+ + OlostepServerError_InvalidEndpointCalled
349
+ + OlostepServerError_ResourceNotFound
350
+ + OlostepServerError_NoResultInResponse
351
+ + OlostepServerError_UnknownIssue
352
+ x OlostepClientError_BaseError ------------------------- <- Client-issued errors
353
+ + OlostepClientError_RequestValidationFailed
354
+ + OlostepClientError_ResponseValidationFailed
355
+ + OlostepClientError_NoAPIKey
356
+ + OlostepClientError_AsyncContext
357
+ + OlostepClientError_BetaFeatureAccessRequired
358
+ + OlostepClientError_Timeout
359
+
360
+ ```