Skip to content

Commit b54b8c2

Browse files
committed
Update API documentation for version 0.10.3
1 parent c131d75 commit b54b8c2

7 files changed

Lines changed: 1595 additions & 32 deletions

File tree

docs/scrapfly/api_response.html

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ <h3>Inherited members</h3>
611611
_secrets = set()
612612

613613
for signing_secret in signing_secrets:
614-
_secrets.add(binascii.unhexlify(signing_secret))
614+
_secrets.add(signing_secret.encode(&#39;utf-8&#39;))
615615

616616
self._signing_secret = tuple(_secrets)
617617

@@ -637,7 +637,18 @@ <h3>Inherited members</h3>
637637

638638
def verify(self, message: bytes, signature: str) -&gt; bool:
639639
for signing_secret in self._signing_secret:
640-
if hmac.new(signing_secret, message, hashlib.sha256).hexdigest().upper() == signature:
640+
computed = hmac.new(signing_secret, message, hashlib.sha256).hexdigest().upper()
641+
logger.debug(
642+
&#39;WEBHOOK_VERIFY_DEBUG key_len=%d key_sha=%s body_len=%d body_sha=%s computed=%s received=%s match=%s&#39;,
643+
len(signing_secret),
644+
hashlib.sha256(signing_secret).hexdigest()[:16],
645+
len(message),
646+
hashlib.sha256(message).hexdigest()[:16],
647+
computed,
648+
signature,
649+
computed == signature,
650+
)
651+
if computed == signature:
641652
return True
642653

643654
return False
@@ -819,7 +830,18 @@ <h3>Methods</h3>
819830
</summary>
820831
<pre><code class="python">def verify(self, message: bytes, signature: str) -&gt; bool:
821832
for signing_secret in self._signing_secret:
822-
if hmac.new(signing_secret, message, hashlib.sha256).hexdigest().upper() == signature:
833+
computed = hmac.new(signing_secret, message, hashlib.sha256).hexdigest().upper()
834+
logger.debug(
835+
&#39;WEBHOOK_VERIFY_DEBUG key_len=%d key_sha=%s body_len=%d body_sha=%s computed=%s received=%s match=%s&#39;,
836+
len(signing_secret),
837+
hashlib.sha256(signing_secret).hexdigest()[:16],
838+
len(message),
839+
hashlib.sha256(message).hexdigest()[:16],
840+
computed,
841+
signature,
842+
computed == signature,
843+
)
844+
if computed == signature:
823845
return True
824846

825847
return False</code></pre>

docs/scrapfly/browser_config.html

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ <h2 class="section-title" id="header-classes">Classes</h2>
4848
<dl>
4949
<dt id="scrapfly.browser_config.BrowserConfig"><code class="flex name class">
5050
<span>class <span class="ident">BrowserConfig</span></span>
51-
<span>(</span><span>proxy_pool: str | <a title="scrapfly.browser_config.ProxyPool" href="#scrapfly.browser_config.ProxyPool">ProxyPool</a> | None = None,<br>os: str | <a title="scrapfly.browser_config.OperatingSystem" href="#scrapfly.browser_config.OperatingSystem">OperatingSystem</a> | None = None,<br>session: str | None = None,<br>country: str | None = None,<br>auto_close: bool | None = None,<br>timeout: int | None = None,<br>debug: bool | None = None,<br>extensions: List[str] | None = None,<br>block_images: bool | None = None,<br>block_styles: bool | None = None,<br>block_fonts: bool | None = None,<br>block_media: bool | None = None,<br>screenshot: bool | None = None,<br>resolution: str | None = None,<br>target_url: str | None = None,<br>cache: bool | None = None,<br>blacklist: bool | None = None,<br>unblock: bool | None = None,<br>unblock_timeout: int | None = None,<br>browser_brand: str | None = None,<br>byop_proxy: str | None = None,<br>enable_mcp: bool | None = None)</span>
51+
<span>(</span><span>proxy_pool: str | <a title="scrapfly.browser_config.ProxyPool" href="#scrapfly.browser_config.ProxyPool">ProxyPool</a> | None = None,<br>os: str | <a title="scrapfly.browser_config.OperatingSystem" href="#scrapfly.browser_config.OperatingSystem">OperatingSystem</a> | None = None,<br>session: str | None = None,<br>country: str | None = None,<br>auto_close: bool | None = None,<br>timeout: int | None = None,<br>debug: bool | None = None,<br>extensions: List[str] | None = None,<br>block_images: bool | None = None,<br>block_styles: bool | None = None,<br>block_fonts: bool | None = None,<br>block_media: bool | None = None,<br>screenshot: bool | None = None,<br>resolution: str | None = None,<br>target_url: str | None = None,<br>cache: bool | None = None,<br>blacklist: bool | None = None,<br>unblock: bool | None = None,<br>unblock_timeout: int | None = None,<br>browser_brand: str | None = None,<br>byop_proxy: str | None = None,<br>enable_mcp: bool | None = None,<br>solve_captcha: bool | None = None)</span>
5252
</code></dt>
5353
<dd>
5454
<details class="source">
@@ -83,6 +83,7 @@ <h2 class="section-title" id="header-classes">Classes</h2>
8383
browser_brand: Optional[str] = None,
8484
byop_proxy: Optional[str] = None,
8585
enable_mcp: Optional[bool] = None,
86+
solve_captcha: Optional[bool] = None,
8687
):
8788
if timeout is not None and timeout &gt; 1800:
8889
raise ValueError(&#39;timeout cannot exceed 1800 seconds (30 minutes)&#39;)
@@ -122,6 +123,12 @@ <h2 class="section-title" id="header-classes">Classes</h2>
122123
# https://scrapfly.io/docs/cloud-browser-api/byop
123124
self.byop_proxy = byop_proxy
124125
self.enable_mcp = enable_mcp
126+
# SolveCaptcha: arm Scrapium&#39;s built-in captcha detector + solver on
127+
# the first page attach. Turnstile, DataDome slider, reCAPTCHA,
128+
# GeeTest, PerimeterX hold, and puzzle captchas are handled
129+
# automatically. Billed per solve; failures cost nothing.
130+
# https://scrapfly.io/docs/cloud-browser-api/captcha-solver
131+
self.solve_captcha = solve_captcha
125132

126133
def websocket_url(self, api_key: str, host: Optional[str] = None) -&gt; str:
127134
params = {&#39;api_key&#39;: api_key}
@@ -192,6 +199,9 @@ <h2 class="section-title" id="header-classes">Classes</h2>
192199
if self.enable_mcp is not None:
193200
params[&#39;enable_mcp&#39;] = self._bool_to_http(self.enable_mcp)
194201

202+
if self.solve_captcha is not None:
203+
params[&#39;solve_captcha&#39;] = self._bool_to_http(self.solve_captcha)
204+
195205
base_host = host or self.CLOUD_BROWSER_HOST
196206
return base_host + &#39;?&#39; + urlencode(params)
197207

@@ -219,6 +229,7 @@ <h2 class="section-title" id="header-classes">Classes</h2>
219229
&#39;browser_brand&#39;: self.browser_brand,
220230
&#39;byop_proxy&#39;: self.byop_proxy,
221231
&#39;enable_mcp&#39;: self.enable_mcp,
232+
&#39;solve_captcha&#39;: self.solve_captcha,
222233
}
223234

224235
@staticmethod
@@ -254,6 +265,7 @@ <h2 class="section-title" id="header-classes">Classes</h2>
254265
browser_brand=browser_config_dict.get(&#39;browser_brand&#39;, None),
255266
byop_proxy=browser_config_dict.get(&#39;byop_proxy&#39;, None),
256267
enable_mcp=browser_config_dict.get(&#39;enable_mcp&#39;, None),
268+
solve_captcha=browser_config_dict.get(&#39;solve_captcha&#39;, None),
257269
)</code></pre>
258270
</details>
259271
<div class="desc"></div>
@@ -311,6 +323,7 @@ <h3>Static methods</h3>
311323
browser_brand=browser_config_dict.get(&#39;browser_brand&#39;, None),
312324
byop_proxy=browser_config_dict.get(&#39;byop_proxy&#39;, None),
313325
enable_mcp=browser_config_dict.get(&#39;enable_mcp&#39;, None),
326+
solve_captcha=browser_config_dict.get(&#39;solve_captcha&#39;, None),
314327
)</code></pre>
315328
</details>
316329
<div class="desc"></div>
@@ -350,6 +363,7 @@ <h3>Methods</h3>
350363
&#39;browser_brand&#39;: self.browser_brand,
351364
&#39;byop_proxy&#39;: self.byop_proxy,
352365
&#39;enable_mcp&#39;: self.enable_mcp,
366+
&#39;solve_captcha&#39;: self.solve_captcha,
353367
}</code></pre>
354368
</details>
355369
<div class="desc"></div>
@@ -431,6 +445,9 @@ <h3>Methods</h3>
431445
if self.enable_mcp is not None:
432446
params[&#39;enable_mcp&#39;] = self._bool_to_http(self.enable_mcp)
433447

448+
if self.solve_captcha is not None:
449+
params[&#39;solve_captcha&#39;] = self._bool_to_http(self.solve_captcha)
450+
434451
base_host = host or self.CLOUD_BROWSER_HOST
435452
return base_host + &#39;?&#39; + urlencode(params)</code></pre>
436453
</details>

docs/scrapfly/client.html

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ <h3>Class variables</h3>
123123
<summary>
124124
<span>Expand source code</span>
125125
</summary>
126-
<pre><code class="python">class ScrapflyClient:
126+
<pre><code class="python">class ScrapflyClient(ScheduleClientMixin):
127127

128128
HOST = &#39;https://api.scrapfly.io&#39;
129129
CLOUD_BROWSER_HOST = &#39;wss://browser.scrapfly.io&#39;
@@ -1674,6 +1674,7 @@ <h3>Class variables</h3>
16741674
body: Optional[str] = None,
16751675
method: Optional[str] = None,
16761676
enable_mcp: Optional[bool] = None,
1677+
solve_captcha: Optional[bool] = None,
16771678
) -&gt; Dict:
16781679
&#34;&#34;&#34;
16791680
Bypass anti-bot protection and get a ready-to-use browser session.
@@ -1722,6 +1723,9 @@ <h3>Class variables</h3>
17221723
if enable_mcp is not None:
17231724
json_body[&#39;enable_mcp&#39;] = enable_mcp
17241725

1726+
if solve_captcha is not None:
1727+
json_body[&#39;solve_captcha&#39;] = solve_captcha
1728+
17251729
response = self._http_handler(
17261730
method=&#39;POST&#39;,
17271731
url=self.cloud_browser_api_host + &#39;/unblock&#39;,
@@ -1931,7 +1935,14 @@ <h3>Class variables</h3>
19311935
response.raise_for_status()
19321936
return response.json()</code></pre>
19331937
</details>
1934-
<div class="desc"></div>
1938+
<div class="desc"><p>Mixed into ScrapflyClient — provides the public schedule surface.</p>
1939+
<p>All methods funnel through <code>_schedule_request</code>, which uses the same
1940+
<code>self._http_handler</code> and <code>self.host</code> / <code>self.key</code> as the rest of the
1941+
client so retries, verify, headers and timeouts behave identically.</p></div>
1942+
<h3>Ancestors</h3>
1943+
<ul class="hlist">
1944+
<li><a title="scrapfly.schedule.ScheduleClientMixin" href="schedule.html#scrapfly.schedule.ScheduleClientMixin">ScheduleClientMixin</a></li>
1945+
</ul>
19351946
<h3>Class variables</h3>
19361947
<dl>
19371948
<dt id="scrapfly.client.ScrapflyClient.CLOUD_BROWSER_API_HOST"><code class="name">var <span class="ident">CLOUD_BROWSER_API_HOST</span></code></dt>
@@ -2577,7 +2588,7 @@ <h2 id="example">Example</h2>
25772588
:return: dict with 'sessions' list and 'total' count</p></div>
25782589
</dd>
25792590
<dt id="scrapfly.client.ScrapflyClient.cloud_browser_unblock"><code class="name flex">
2580-
<span>def <span class="ident">cloud_browser_unblock</span></span>(<span>self,<br>url: str,<br>proxy_pool: str | None = None,<br>country: str | None = None,<br>os: str | None = None,<br>timeout: int | None = None,<br>browser_timeout: int | None = None,<br>headers: Dict | None = None,<br>body: str | None = None,<br>method: str | None = None,<br>enable_mcp: bool | None = None) ‑> Dict</span>
2591+
<span>def <span class="ident">cloud_browser_unblock</span></span>(<span>self,<br>url: str,<br>proxy_pool: str | None = None,<br>country: str | None = None,<br>os: str | None = None,<br>timeout: int | None = None,<br>browser_timeout: int | None = None,<br>headers: Dict | None = None,<br>body: str | None = None,<br>method: str | None = None,<br>enable_mcp: bool | None = None,<br>solve_captcha: bool | None = None) ‑> Dict</span>
25812592
</code></dt>
25822593
<dd>
25832594
<details class="source">
@@ -2596,6 +2607,7 @@ <h2 id="example">Example</h2>
25962607
body: Optional[str] = None,
25972608
method: Optional[str] = None,
25982609
enable_mcp: Optional[bool] = None,
2610+
solve_captcha: Optional[bool] = None,
25992611
) -&gt; Dict:
26002612
&#34;&#34;&#34;
26012613
Bypass anti-bot protection and get a ready-to-use browser session.
@@ -2644,6 +2656,9 @@ <h2 id="example">Example</h2>
26442656
if enable_mcp is not None:
26452657
json_body[&#39;enable_mcp&#39;] = enable_mcp
26462658

2659+
if solve_captcha is not None:
2660+
json_body[&#39;solve_captcha&#39;] = solve_captcha
2661+
26472662
response = self._http_handler(
26482663
method=&#39;POST&#39;,
26492664
url=self.cloud_browser_api_host + &#39;/unblock&#39;,
@@ -3973,6 +3988,21 @@ <h2 id="example">Example</h2>
39733988
</code></pre></div>
39743989
</dd>
39753990
</dl>
3991+
<h3>Inherited members</h3>
3992+
<ul class="hlist">
3993+
<li><code><b><a title="scrapfly.schedule.ScheduleClientMixin" href="schedule.html#scrapfly.schedule.ScheduleClientMixin">ScheduleClientMixin</a></b></code>:
3994+
<ul class="hlist">
3995+
<li><code><a title="scrapfly.schedule.ScheduleClientMixin.cancel_schedule" href="schedule.html#scrapfly.schedule.ScheduleClientMixin.cancel_schedule">cancel_schedule</a></code></li>
3996+
<li><code><a title="scrapfly.schedule.ScheduleClientMixin.create_crawler_schedule" href="schedule.html#scrapfly.schedule.ScheduleClientMixin.create_crawler_schedule">create_crawler_schedule</a></code></li>
3997+
<li><code><a title="scrapfly.schedule.ScheduleClientMixin.create_scrape_schedule" href="schedule.html#scrapfly.schedule.ScheduleClientMixin.create_scrape_schedule">create_scrape_schedule</a></code></li>
3998+
<li><code><a title="scrapfly.schedule.ScheduleClientMixin.create_screenshot_schedule" href="schedule.html#scrapfly.schedule.ScheduleClientMixin.create_screenshot_schedule">create_screenshot_schedule</a></code></li>
3999+
<li><code><a title="scrapfly.schedule.ScheduleClientMixin.execute_schedule" href="schedule.html#scrapfly.schedule.ScheduleClientMixin.execute_schedule">execute_schedule</a></code></li>
4000+
<li><code><a title="scrapfly.schedule.ScheduleClientMixin.get_schedule" href="schedule.html#scrapfly.schedule.ScheduleClientMixin.get_schedule">get_schedule</a></code></li>
4001+
<li><code><a title="scrapfly.schedule.ScheduleClientMixin.list_schedules" href="schedule.html#scrapfly.schedule.ScheduleClientMixin.list_schedules">list_schedules</a></code></li>
4002+
<li><code><a title="scrapfly.schedule.ScheduleClientMixin.update_schedule" href="schedule.html#scrapfly.schedule.ScheduleClientMixin.update_schedule">update_schedule</a></code></li>
4003+
</ul>
4004+
</li>
4005+
</ul>
39764006
</dd>
39774007
</dl>
39784008
</section>

docs/scrapfly/crawler/crawler_webhook.html

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,9 @@ <h2 class="section-title" id="header-functions">Functions</h2>
9090
Args:
9191
payload: The full webhook body as a dict (i.e. what you get from
9292
``request.json``).
93-
signing_secrets: Optional tuple of signing secrets (hex strings) for
94-
signature verification.
93+
signing_secrets: Optional tuple of signing secrets for signature
94+
verification. Pass each secret as it appears in the webhook
95+
dashboard (UTF-8 string, not hex-encoded).
9596
signature: Optional webhook signature header value
9697
(``X-Scrapfly-Webhook-Signature``).
9798

@@ -111,7 +112,7 @@ <h2 class="section-title" id="header-functions">Functions</h2>
111112
... def handle_webhook():
112113
... wh = webhook_from_payload(
113114
... request.json,
114-
... signing_secrets=(&#39;your-secret-hex&#39;,),
115+
... signing_secrets=(&#39;YOUR-WEBHOOK-SIGNING-SECRET&#39;,),
115116
... signature=request.headers.get(&#39;X-Scrapfly-Webhook-Signature&#39;),
116117
... )
117118
... if isinstance(wh, CrawlerLifecycleWebhook) and wh.event == &#39;crawler_finished&#39;:
@@ -151,8 +152,9 @@ <h2 id="args">Args</h2>
151152
<dd>The full webhook body as a dict (i.e. what you get from
152153
<code>request.json</code>).</dd>
153154
<dt><strong><code>signing_secrets</code></strong></dt>
154-
<dd>Optional tuple of signing secrets (hex strings) for
155-
signature verification.</dd>
155+
<dd>Optional tuple of signing secrets for signature
156+
verification. Pass each secret as it appears in the webhook
157+
dashboard (UTF-8 string, not hex-encoded).</dd>
156158
<dt><strong><code>signature</code></strong></dt>
157159
<dd>Optional webhook signature header value
158160
(<code>X-Scrapfly-Webhook-Signature</code>).</dd>
@@ -176,7 +178,7 @@ <h2 id="example">Example</h2>
176178
... def handle_webhook():
177179
... wh = webhook_from_payload(
178180
... request.json,
179-
... signing_secrets=('your-secret-hex',),
181+
... signing_secrets=('YOUR-WEBHOOK-SIGNING-SECRET',),
180182
... signature=request.headers.get('X-Scrapfly-Webhook-Signature'),
181183
... )
182184
... if isinstance(wh, CrawlerLifecycleWebhook) and wh.event == 'crawler_finished':

docs/scrapfly/crawler/index.html

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,9 @@ <h2 id="example">Example</h2>
155155
Args:
156156
payload: The full webhook body as a dict (i.e. what you get from
157157
``request.json``).
158-
signing_secrets: Optional tuple of signing secrets (hex strings) for
159-
signature verification.
158+
signing_secrets: Optional tuple of signing secrets for signature
159+
verification. Pass each secret as it appears in the webhook
160+
dashboard (UTF-8 string, not hex-encoded).
160161
signature: Optional webhook signature header value
161162
(``X-Scrapfly-Webhook-Signature``).
162163

@@ -176,7 +177,7 @@ <h2 id="example">Example</h2>
176177
... def handle_webhook():
177178
... wh = webhook_from_payload(
178179
... request.json,
179-
... signing_secrets=(&#39;your-secret-hex&#39;,),
180+
... signing_secrets=(&#39;YOUR-WEBHOOK-SIGNING-SECRET&#39;,),
180181
... signature=request.headers.get(&#39;X-Scrapfly-Webhook-Signature&#39;),
181182
... )
182183
... if isinstance(wh, CrawlerLifecycleWebhook) and wh.event == &#39;crawler_finished&#39;:
@@ -216,8 +217,9 @@ <h2 id="args">Args</h2>
216217
<dd>The full webhook body as a dict (i.e. what you get from
217218
<code>request.json</code>).</dd>
218219
<dt><strong><code>signing_secrets</code></strong></dt>
219-
<dd>Optional tuple of signing secrets (hex strings) for
220-
signature verification.</dd>
220+
<dd>Optional tuple of signing secrets for signature
221+
verification. Pass each secret as it appears in the webhook
222+
dashboard (UTF-8 string, not hex-encoded).</dd>
221223
<dt><strong><code>signature</code></strong></dt>
222224
<dd>Optional webhook signature header value
223225
(<code>X-Scrapfly-Webhook-Signature</code>).</dd>
@@ -241,7 +243,7 @@ <h2 id="example">Example</h2>
241243
... def handle_webhook():
242244
... wh = webhook_from_payload(
243245
... request.json,
244-
... signing_secrets=('your-secret-hex',),
246+
... signing_secrets=('YOUR-WEBHOOK-SIGNING-SECRET',),
245247
... signature=request.headers.get('X-Scrapfly-Webhook-Signature'),
246248
... )
247249
... if isinstance(wh, CrawlerLifecycleWebhook) and wh.event == 'crawler_finished':

0 commit comments

Comments
 (0)