apivault-substack-archive-scraper 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.
- apivault_substack_archive_scraper-0.1.0/LICENSE +21 -0
- apivault_substack_archive_scraper-0.1.0/PKG-INFO +72 -0
- apivault_substack_archive_scraper-0.1.0/README.md +57 -0
- apivault_substack_archive_scraper-0.1.0/apivault_substack_archive_scraper/__init__.py +4 -0
- apivault_substack_archive_scraper-0.1.0/apivault_substack_archive_scraper/client.py +113 -0
- apivault_substack_archive_scraper-0.1.0/apivault_substack_archive_scraper.egg-info/PKG-INFO +72 -0
- apivault_substack_archive_scraper-0.1.0/apivault_substack_archive_scraper.egg-info/SOURCES.txt +9 -0
- apivault_substack_archive_scraper-0.1.0/apivault_substack_archive_scraper.egg-info/dependency_links.txt +1 -0
- apivault_substack_archive_scraper-0.1.0/apivault_substack_archive_scraper.egg-info/top_level.txt +1 -0
- apivault_substack_archive_scraper-0.1.0/pyproject.toml +22 -0
- apivault_substack_archive_scraper-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ApiVault Labs
|
|
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,72 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apivault-substack-archive-scraper
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for Substack Archive Scraper on Apify. Run, resume and download results with diagnostics.
|
|
5
|
+
Author: ApiVault Labs
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://apify.com/apivault_labs/substack-archive-scraper
|
|
8
|
+
Project-URL: Source, https://github.com/apivault-labs/apivault-labs/tree/main/packages/apivault-substack-archive-scraper
|
|
9
|
+
Project-URL: Documentation, https://github.com/apivault-labs/apivault-labs/tree/main/packages/apivault-substack-archive-scraper#readme
|
|
10
|
+
Keywords: apify,python-sdk,substack,archive,scraper
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# Substack Archive Scraper - Python client
|
|
17
|
+
|
|
18
|
+
Collect public Substack archive posts, dates, URLs and optional public article text.
|
|
19
|
+
|
|
20
|
+
This client starts the hosted [Apify Actor](https://apify.com/apivault_labs/substack-archive-scraper), waits for completion and downloads Dataset records. It also reads SUMMARY and ERRORS when available. Source coverage and result fields depend on the Actor.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install apivault-substack-archive-scraper
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Run
|
|
29
|
+
|
|
30
|
+
Set `APIFY_TOKEN` in your environment. Replace the example values with your own valid inputs from the [Actor input form](https://apify.com/apivault_labs/substack-archive-scraper/input).
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from apivault_substack_archive_scraper import Client, WaitTimeout
|
|
34
|
+
|
|
35
|
+
client = Client()
|
|
36
|
+
actor_input = {
|
|
37
|
+
"urls": [
|
|
38
|
+
"https://www.experimental-history.com"
|
|
39
|
+
],
|
|
40
|
+
"maxItems": 5
|
|
41
|
+
}
|
|
42
|
+
result = client.run(actor_input, max_total_charge_usd=0.05, max_rows=100)
|
|
43
|
+
print(result.run_id, len(result.rows), result.truncated)
|
|
44
|
+
print(result.summary)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
An Apify account and token are required. The package has no subscription of its own; hosted runs are billed by Apify under the Actor pricing. The event charge cap may not include platform usage. Review the [current price](https://apify.com/apivault_labs/substack-archive-scraper/pricing) before changing it. `max_rows` limits the download, not the Actor collection or its charges; configure an Actor result limit too.
|
|
48
|
+
|
|
49
|
+
## Resume and diagnostics
|
|
50
|
+
|
|
51
|
+
`client.start(input)` returns a run ID; `client.wait(run_id)` and `client.result(run_id)` resume an existing run. A `WaitTimeout` exposes `run_id`. Starting a run is never automatically retried. Use an existing run ID after a connection or waiting failure to avoid duplicate paid runs. Failed and aborted runs raise `ActorError`. No-match and partial results require inspecting `summary` and `errors`; diagnostic rows must not be mistaken for useful results.
|
|
52
|
+
|
|
53
|
+
Rows can be serialized as JSON or used in your own CSV/Excel pipeline. Native Dataset exports are available in Apify. Optional integrations and credentials depend on the Actor.
|
|
54
|
+
|
|
55
|
+
[Hosted Actor](https://apify.com/apivault_labs/substack-archive-scraper) | [Source](https://github.com/apivault-labs/apivault-labs/tree/main/packages/apivault-substack-archive-scraper) | [PyPI](https://pypi.org/project/apivault-substack-archive-scraper/)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## Related integration
|
|
59
|
+
|
|
60
|
+
[Python client](https://github.com/apivault-labs/apivault-labs/tree/main/packages/apivault-substack-archive-scraper) · [PyPI](https://pypi.org/project/apivault-substack-archive-scraper/) · [n8n source and workflow](https://github.com/apivault-labs/apivault-labs/tree/main/community-nodes/n8n-nodes-apivault-substack-archive-scraper) · [npm](https://www.npmjs.com/package/n8n-nodes-apivault-substack-archive-scraper)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
## Source coverage and limits
|
|
64
|
+
|
|
65
|
+
Public archives and explicit post URLs only. Paid posts provide metadata and a short preview; no subscriber content, login or paywall bypass. Inspect source diagnostics and continuation limits. Reading a saved run does not continue the archive; copy continuation inputs into a new separately billed run.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## Collection and spend limits
|
|
69
|
+
|
|
70
|
+
The small example sets `maxItems` to bound paid collection. `max_rows` / Maximum Rows to Read limits only the dataset download. The event charge cap does not cap infrastructure or proxy usage. The clients do not start an automatic paid retry after a start request fails. Save the run ID and inspect that run before starting another. For source failures, inspect SUMMARY and ERRORS in Apify Console; Client.result only returns successful runs.
|
|
71
|
+
|
|
72
|
+
The hosted Actor also charges an Actor Start event at USD 0.00005 per GB of configured memory, minimum one event, in addition to delivered-result events and infrastructure usage. Empty or failed runs can still incur start and resource costs.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Substack Archive Scraper - Python client
|
|
2
|
+
|
|
3
|
+
Collect public Substack archive posts, dates, URLs and optional public article text.
|
|
4
|
+
|
|
5
|
+
This client starts the hosted [Apify Actor](https://apify.com/apivault_labs/substack-archive-scraper), waits for completion and downloads Dataset records. It also reads SUMMARY and ERRORS when available. Source coverage and result fields depend on the Actor.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install apivault-substack-archive-scraper
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Run
|
|
14
|
+
|
|
15
|
+
Set `APIFY_TOKEN` in your environment. Replace the example values with your own valid inputs from the [Actor input form](https://apify.com/apivault_labs/substack-archive-scraper/input).
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from apivault_substack_archive_scraper import Client, WaitTimeout
|
|
19
|
+
|
|
20
|
+
client = Client()
|
|
21
|
+
actor_input = {
|
|
22
|
+
"urls": [
|
|
23
|
+
"https://www.experimental-history.com"
|
|
24
|
+
],
|
|
25
|
+
"maxItems": 5
|
|
26
|
+
}
|
|
27
|
+
result = client.run(actor_input, max_total_charge_usd=0.05, max_rows=100)
|
|
28
|
+
print(result.run_id, len(result.rows), result.truncated)
|
|
29
|
+
print(result.summary)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
An Apify account and token are required. The package has no subscription of its own; hosted runs are billed by Apify under the Actor pricing. The event charge cap may not include platform usage. Review the [current price](https://apify.com/apivault_labs/substack-archive-scraper/pricing) before changing it. `max_rows` limits the download, not the Actor collection or its charges; configure an Actor result limit too.
|
|
33
|
+
|
|
34
|
+
## Resume and diagnostics
|
|
35
|
+
|
|
36
|
+
`client.start(input)` returns a run ID; `client.wait(run_id)` and `client.result(run_id)` resume an existing run. A `WaitTimeout` exposes `run_id`. Starting a run is never automatically retried. Use an existing run ID after a connection or waiting failure to avoid duplicate paid runs. Failed and aborted runs raise `ActorError`. No-match and partial results require inspecting `summary` and `errors`; diagnostic rows must not be mistaken for useful results.
|
|
37
|
+
|
|
38
|
+
Rows can be serialized as JSON or used in your own CSV/Excel pipeline. Native Dataset exports are available in Apify. Optional integrations and credentials depend on the Actor.
|
|
39
|
+
|
|
40
|
+
[Hosted Actor](https://apify.com/apivault_labs/substack-archive-scraper) | [Source](https://github.com/apivault-labs/apivault-labs/tree/main/packages/apivault-substack-archive-scraper) | [PyPI](https://pypi.org/project/apivault-substack-archive-scraper/)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## Related integration
|
|
44
|
+
|
|
45
|
+
[Python client](https://github.com/apivault-labs/apivault-labs/tree/main/packages/apivault-substack-archive-scraper) · [PyPI](https://pypi.org/project/apivault-substack-archive-scraper/) · [n8n source and workflow](https://github.com/apivault-labs/apivault-labs/tree/main/community-nodes/n8n-nodes-apivault-substack-archive-scraper) · [npm](https://www.npmjs.com/package/n8n-nodes-apivault-substack-archive-scraper)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## Source coverage and limits
|
|
49
|
+
|
|
50
|
+
Public archives and explicit post URLs only. Paid posts provide metadata and a short preview; no subscriber content, login or paywall bypass. Inspect source diagnostics and continuation limits. Reading a saved run does not continue the archive; copy continuation inputs into a new separately billed run.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## Collection and spend limits
|
|
54
|
+
|
|
55
|
+
The small example sets `maxItems` to bound paid collection. `max_rows` / Maximum Rows to Read limits only the dataset download. The event charge cap does not cap infrastructure or proxy usage. The clients do not start an automatic paid retry after a start request fails. Save the run ID and inspect that run before starting another. For source failures, inspect SUMMARY and ERRORS in Apify Console; Client.result only returns successful runs.
|
|
56
|
+
|
|
57
|
+
The hosted Actor also charges an Actor Start event at USD 0.00005 per GB of configured memory, minimum one event, in addition to delivered-result events and infrastructure usage. Empty or failed runs can still incur start and resource costs.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Thin client for the hosted substack-archive-scraper Apify Actor."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
import json, os, re, time, urllib.error, urllib.parse, urllib.request
|
|
5
|
+
from typing import Any, Mapping
|
|
6
|
+
|
|
7
|
+
ACTOR_ID = "nJFN711cBZgSpDITt"
|
|
8
|
+
API = "https://api.apify.com/v2"
|
|
9
|
+
|
|
10
|
+
class ActorError(RuntimeError):
|
|
11
|
+
"""The API rejected a request or the run did not succeed."""
|
|
12
|
+
|
|
13
|
+
class WaitTimeout(ActorError):
|
|
14
|
+
"""Waiting ended; resume this run instead of starting a duplicate."""
|
|
15
|
+
def __init__(self, run_id: str):
|
|
16
|
+
self.run_id = run_id
|
|
17
|
+
super().__init__(f"Waiting timed out; resume run {run_id}")
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class RunResult:
|
|
21
|
+
run_id: str
|
|
22
|
+
dataset_id: str
|
|
23
|
+
status: str
|
|
24
|
+
rows: list[dict[str, Any]]
|
|
25
|
+
summary: Any
|
|
26
|
+
errors: Any
|
|
27
|
+
truncated: bool
|
|
28
|
+
|
|
29
|
+
class Client:
|
|
30
|
+
"""Start one hosted run, wait safely, and read rows plus diagnostics."""
|
|
31
|
+
def __init__(self, token: str | None = None):
|
|
32
|
+
self._token = (token or os.environ.get("APIFY_TOKEN") or os.environ.get("APIFY_API_TOKEN") or "").strip()
|
|
33
|
+
if not self._token:
|
|
34
|
+
raise ValueError("Provide an Apify token or set APIFY_TOKEN")
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def _id(value: str) -> str:
|
|
38
|
+
if not isinstance(value, str) or not re.fullmatch(r"[A-Za-z0-9_-]{1,100}", value):
|
|
39
|
+
raise ValueError("Invalid Apify identifier")
|
|
40
|
+
return value
|
|
41
|
+
|
|
42
|
+
def _request(self, method: str, path: str, body: Any = None, params: dict | None = None, optional: bool = False) -> Any:
|
|
43
|
+
url = API + path + ("?" + urllib.parse.urlencode(params) if params else "")
|
|
44
|
+
request = urllib.request.Request(url, method=method,
|
|
45
|
+
data=json.dumps(body).encode("utf-8") if body is not None else None,
|
|
46
|
+
headers={"Authorization": "Bearer " + self._token, "Content-Type": "application/json", "User-Agent": "apivault-substack-archive-scraper/0.1.0"})
|
|
47
|
+
try:
|
|
48
|
+
with urllib.request.urlopen(request, timeout=30) as response:
|
|
49
|
+
raw = response.read()
|
|
50
|
+
return json.loads(raw) if raw else None
|
|
51
|
+
except urllib.error.HTTPError as error:
|
|
52
|
+
if optional and error.code == 404:
|
|
53
|
+
return None
|
|
54
|
+
raise ActorError(f"Apify request failed with HTTP {error.code}") from None
|
|
55
|
+
except (urllib.error.URLError, TimeoutError):
|
|
56
|
+
raise ActorError("Apify request could not complete; check an existing run before retrying") from None
|
|
57
|
+
|
|
58
|
+
def start(self, actor_input: Mapping[str, Any], *, max_total_charge_usd: float = 0.05, actor_timeout_secs: int = 300) -> dict[str, Any]:
|
|
59
|
+
if not isinstance(actor_input, Mapping):
|
|
60
|
+
raise TypeError("actor_input must be a mapping")
|
|
61
|
+
if not 0 < max_total_charge_usd <= 1000 or not 1 <= actor_timeout_secs <= 3600:
|
|
62
|
+
raise ValueError("Use a positive charge cap and timeout of 1–3600 seconds")
|
|
63
|
+
# POST is never automatically retried: a retry could start a paid duplicate.
|
|
64
|
+
response = self._request("POST", f"/acts/{ACTOR_ID}/runs", dict(actor_input),
|
|
65
|
+
{"maxTotalChargeUsd": max_total_charge_usd, "timeout": actor_timeout_secs, "build": "latest"})
|
|
66
|
+
run = response.get("data", {}) if isinstance(response, dict) else {}
|
|
67
|
+
if not run.get("id"):
|
|
68
|
+
raise ActorError("Apify did not return a run ID; check Console before retrying")
|
|
69
|
+
return run
|
|
70
|
+
|
|
71
|
+
def wait(self, run_id: str, *, wait_secs: float = 600, poll_interval: float = 3) -> dict[str, Any]:
|
|
72
|
+
run_id = self._id(run_id)
|
|
73
|
+
if wait_secs <= 0 or poll_interval <= 0:
|
|
74
|
+
raise ValueError("wait_secs and poll_interval must be positive")
|
|
75
|
+
deadline = time.monotonic() + wait_secs
|
|
76
|
+
while time.monotonic() < deadline:
|
|
77
|
+
run = self._request("GET", f"/actor-runs/{run_id}")["data"]
|
|
78
|
+
if run["status"] == "SUCCEEDED":
|
|
79
|
+
return run
|
|
80
|
+
if run["status"] in {"FAILED", "ABORTED", "TIMED-OUT"}:
|
|
81
|
+
raise ActorError(f"Run {run_id} ended with status {run['status']}")
|
|
82
|
+
time.sleep(min(poll_interval, max(0, deadline-time.monotonic())))
|
|
83
|
+
raise WaitTimeout(run_id)
|
|
84
|
+
|
|
85
|
+
def result(self, run_id: str, *, max_rows: int = 1000) -> RunResult:
|
|
86
|
+
run_id = self._id(run_id)
|
|
87
|
+
if not isinstance(max_rows, int) or not 1 <= max_rows <= 100000:
|
|
88
|
+
raise ValueError("max_rows must be an integer from 1 to 100000")
|
|
89
|
+
run = self._request("GET", f"/actor-runs/{run_id}")["data"]
|
|
90
|
+
if run["status"] != "SUCCEEDED":
|
|
91
|
+
raise ActorError(f"Run {run_id} is {run['status']}; wait before reading results")
|
|
92
|
+
dataset = self._id(run["defaultDatasetId"])
|
|
93
|
+
metadata = self._request("GET", f"/datasets/{dataset}")["data"]
|
|
94
|
+
rows: list[dict[str, Any]] = []
|
|
95
|
+
while len(rows) < max_rows:
|
|
96
|
+
limit = min(1000, max_rows-len(rows))
|
|
97
|
+
page = self._request("GET", f"/datasets/{dataset}/items", params={"clean": "true", "limit": limit, "offset": len(rows)})
|
|
98
|
+
if not isinstance(page, list):
|
|
99
|
+
raise ActorError("Apify returned an unexpected Dataset response")
|
|
100
|
+
if len(page) > limit:
|
|
101
|
+
raise ActorError("Dataset response exceeded the requested page limit")
|
|
102
|
+
rows.extend(page)
|
|
103
|
+
if len(page) < limit:
|
|
104
|
+
break
|
|
105
|
+
store = self._id(run["defaultKeyValueStoreId"])
|
|
106
|
+
summary = self._request("GET", f"/key-value-stores/{store}/records/SUMMARY", optional=True)
|
|
107
|
+
errors = self._request("GET", f"/key-value-stores/{store}/records/ERRORS", optional=True)
|
|
108
|
+
return RunResult(run_id, dataset, run["status"], rows, summary, errors, metadata.get("itemCount", len(rows)) > len(rows))
|
|
109
|
+
|
|
110
|
+
def run(self, actor_input: Mapping[str, Any], *, max_total_charge_usd: float = 0.05, actor_timeout_secs: int = 300, wait_secs: float = 600, max_rows: int = 1000) -> RunResult:
|
|
111
|
+
run = self.start(actor_input, max_total_charge_usd=max_total_charge_usd, actor_timeout_secs=actor_timeout_secs)
|
|
112
|
+
self.wait(run["id"], wait_secs=wait_secs)
|
|
113
|
+
return self.result(run["id"], max_rows=max_rows)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apivault-substack-archive-scraper
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for Substack Archive Scraper on Apify. Run, resume and download results with diagnostics.
|
|
5
|
+
Author: ApiVault Labs
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://apify.com/apivault_labs/substack-archive-scraper
|
|
8
|
+
Project-URL: Source, https://github.com/apivault-labs/apivault-labs/tree/main/packages/apivault-substack-archive-scraper
|
|
9
|
+
Project-URL: Documentation, https://github.com/apivault-labs/apivault-labs/tree/main/packages/apivault-substack-archive-scraper#readme
|
|
10
|
+
Keywords: apify,python-sdk,substack,archive,scraper
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# Substack Archive Scraper - Python client
|
|
17
|
+
|
|
18
|
+
Collect public Substack archive posts, dates, URLs and optional public article text.
|
|
19
|
+
|
|
20
|
+
This client starts the hosted [Apify Actor](https://apify.com/apivault_labs/substack-archive-scraper), waits for completion and downloads Dataset records. It also reads SUMMARY and ERRORS when available. Source coverage and result fields depend on the Actor.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install apivault-substack-archive-scraper
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Run
|
|
29
|
+
|
|
30
|
+
Set `APIFY_TOKEN` in your environment. Replace the example values with your own valid inputs from the [Actor input form](https://apify.com/apivault_labs/substack-archive-scraper/input).
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from apivault_substack_archive_scraper import Client, WaitTimeout
|
|
34
|
+
|
|
35
|
+
client = Client()
|
|
36
|
+
actor_input = {
|
|
37
|
+
"urls": [
|
|
38
|
+
"https://www.experimental-history.com"
|
|
39
|
+
],
|
|
40
|
+
"maxItems": 5
|
|
41
|
+
}
|
|
42
|
+
result = client.run(actor_input, max_total_charge_usd=0.05, max_rows=100)
|
|
43
|
+
print(result.run_id, len(result.rows), result.truncated)
|
|
44
|
+
print(result.summary)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
An Apify account and token are required. The package has no subscription of its own; hosted runs are billed by Apify under the Actor pricing. The event charge cap may not include platform usage. Review the [current price](https://apify.com/apivault_labs/substack-archive-scraper/pricing) before changing it. `max_rows` limits the download, not the Actor collection or its charges; configure an Actor result limit too.
|
|
48
|
+
|
|
49
|
+
## Resume and diagnostics
|
|
50
|
+
|
|
51
|
+
`client.start(input)` returns a run ID; `client.wait(run_id)` and `client.result(run_id)` resume an existing run. A `WaitTimeout` exposes `run_id`. Starting a run is never automatically retried. Use an existing run ID after a connection or waiting failure to avoid duplicate paid runs. Failed and aborted runs raise `ActorError`. No-match and partial results require inspecting `summary` and `errors`; diagnostic rows must not be mistaken for useful results.
|
|
52
|
+
|
|
53
|
+
Rows can be serialized as JSON or used in your own CSV/Excel pipeline. Native Dataset exports are available in Apify. Optional integrations and credentials depend on the Actor.
|
|
54
|
+
|
|
55
|
+
[Hosted Actor](https://apify.com/apivault_labs/substack-archive-scraper) | [Source](https://github.com/apivault-labs/apivault-labs/tree/main/packages/apivault-substack-archive-scraper) | [PyPI](https://pypi.org/project/apivault-substack-archive-scraper/)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## Related integration
|
|
59
|
+
|
|
60
|
+
[Python client](https://github.com/apivault-labs/apivault-labs/tree/main/packages/apivault-substack-archive-scraper) · [PyPI](https://pypi.org/project/apivault-substack-archive-scraper/) · [n8n source and workflow](https://github.com/apivault-labs/apivault-labs/tree/main/community-nodes/n8n-nodes-apivault-substack-archive-scraper) · [npm](https://www.npmjs.com/package/n8n-nodes-apivault-substack-archive-scraper)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
## Source coverage and limits
|
|
64
|
+
|
|
65
|
+
Public archives and explicit post URLs only. Paid posts provide metadata and a short preview; no subscriber content, login or paywall bypass. Inspect source diagnostics and continuation limits. Reading a saved run does not continue the archive; copy continuation inputs into a new separately billed run.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## Collection and spend limits
|
|
69
|
+
|
|
70
|
+
The small example sets `maxItems` to bound paid collection. `max_rows` / Maximum Rows to Read limits only the dataset download. The event charge cap does not cap infrastructure or proxy usage. The clients do not start an automatic paid retry after a start request fails. Save the run ID and inspect that run before starting another. For source failures, inspect SUMMARY and ERRORS in Apify Console; Client.result only returns successful runs.
|
|
71
|
+
|
|
72
|
+
The hosted Actor also charges an Actor Start event at USD 0.00005 per GB of configured memory, minimum one event, in addition to delivered-result events and infrastructure usage. Empty or failed runs can still incur start and resource costs.
|
apivault_substack_archive_scraper-0.1.0/apivault_substack_archive_scraper.egg-info/SOURCES.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
apivault_substack_archive_scraper/__init__.py
|
|
5
|
+
apivault_substack_archive_scraper/client.py
|
|
6
|
+
apivault_substack_archive_scraper.egg-info/PKG-INFO
|
|
7
|
+
apivault_substack_archive_scraper.egg-info/SOURCES.txt
|
|
8
|
+
apivault_substack_archive_scraper.egg-info/dependency_links.txt
|
|
9
|
+
apivault_substack_archive_scraper.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
apivault_substack_archive_scraper-0.1.0/apivault_substack_archive_scraper.egg-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
apivault_substack_archive_scraper
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "apivault-substack-archive-scraper"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python client for Substack Archive Scraper on Apify. Run, resume and download results with diagnostics."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{name="ApiVault Labs"}]
|
|
14
|
+
keywords = ["apify", "python-sdk", "substack", "archive", "scraper"]
|
|
15
|
+
|
|
16
|
+
[project.urls]
|
|
17
|
+
Homepage = "https://apify.com/apivault_labs/substack-archive-scraper"
|
|
18
|
+
Source = "https://github.com/apivault-labs/apivault-labs/tree/main/packages/apivault-substack-archive-scraper"
|
|
19
|
+
Documentation = "https://github.com/apivault-labs/apivault-labs/tree/main/packages/apivault-substack-archive-scraper#readme"
|
|
20
|
+
|
|
21
|
+
[tool.setuptools.packages.find]
|
|
22
|
+
include = ["apivault_substack_archive_scraper*"]
|