diff --git a/.stats.yml b/.stats.yml
index 86c13449517..5e87bf85241 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,2 +1,2 @@
-configured_endpoints: 1475
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare%2Fcloudflare-663ea50351c42f1c51f49f099c888745f6a06cf22b8fe46cf1fa9ae89fdfdd61.yml
+configured_endpoints: 1483
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cloudflare%2Fcloudflare-f9dc65d353b377858f3d63d594de47559ed3a9d562295719690867315b8ec2fe.yml
diff --git a/api.md b/api.md
index 3a11184648f..a5cf1ff287a 100644
--- a/api.md
+++ b/api.md
@@ -6594,6 +6594,51 @@ Methods:
- client.vectorize.indexes.metadata_index.list(index_name, \*, account_id) -> Optional[MetadataIndexListResponse]
- client.vectorize.indexes.metadata_index.delete(index_name, \*, account_id, \*\*params) -> Optional[MetadataIndexDeleteResponse]
+# URLScanner
+
+Types:
+
+```python
+from cloudflare.types.url_scanner import URLScannerDomain, URLScannerTask
+```
+
+## Responses
+
+Types:
+
+```python
+from cloudflare.types.url_scanner import ResponseGetResponse
+```
+
+Methods:
+
+- client.url_scanner.responses.get(response_id, \*, account_id) -> str
+
+## Scans
+
+Types:
+
+```python
+from cloudflare.types.url_scanner import (
+ ScanCreateResponse,
+ ScanListResponse,
+ ScanBulkCreateResponse,
+ ScanDOMResponse,
+ ScanGetResponse,
+ ScanHARResponse,
+)
+```
+
+Methods:
+
+- client.url_scanner.scans.create(\*, account_id, \*\*params) -> str
+- client.url_scanner.scans.list(\*, account_id, \*\*params) -> ScanListResponse
+- client.url_scanner.scans.bulk_create(\*, account_id, \*\*params) -> ScanBulkCreateResponse
+- client.url_scanner.scans.dom(scan_id, \*, account_id) -> str
+- client.url_scanner.scans.get(scan_id, \*, account_id) -> ScanGetResponse
+- client.url_scanner.scans.har(scan_id, \*, account_id) -> ScanHARResponse
+- client.url_scanner.scans.screenshot(scan_id, \*, account_id, \*\*params) -> BinaryAPIResponse
+
# Radar
## AI
diff --git a/src/cloudflare/_client.py b/src/cloudflare/_client.py
index 8b18c73296e..f48b5ae2359 100644
--- a/src/cloudflare/_client.py
+++ b/src/cloudflare/_client.py
@@ -88,6 +88,7 @@
memberships,
page_shield,
rate_limits,
+ url_scanner,
dns_firewall,
healthchecks,
security_txt,
@@ -185,6 +186,7 @@
from .resources.botnet_feed.botnet_feed import BotnetFeedResource, AsyncBotnetFeedResource
from .resources.diagnostics.diagnostics import DiagnosticsResource, AsyncDiagnosticsResource
from .resources.page_shield.page_shield import PageShieldResource, AsyncPageShieldResource
+ from .resources.url_scanner.url_scanner import URLScannerResource, AsyncURLScannerResource
from .resources.dns_firewall.dns_firewall import DNSFirewallResource, AsyncDNSFirewallResource
from .resources.healthchecks.healthchecks import HealthchecksResource, AsyncHealthchecksResource
from .resources.email_routing.email_routing import EmailRoutingResource, AsyncEmailRoutingResource
@@ -704,6 +706,12 @@ def vectorize(self) -> VectorizeResource:
return VectorizeResource(self)
+ @cached_property
+ def url_scanner(self) -> URLScannerResource:
+ from .resources.url_scanner import URLScannerResource
+
+ return URLScannerResource(self)
+
@cached_property
def radar(self) -> RadarResource:
from .resources.radar import RadarResource
@@ -1469,6 +1477,12 @@ def vectorize(self) -> AsyncVectorizeResource:
return AsyncVectorizeResource(self)
+ @cached_property
+ def url_scanner(self) -> AsyncURLScannerResource:
+ from .resources.url_scanner import AsyncURLScannerResource
+
+ return AsyncURLScannerResource(self)
+
@cached_property
def radar(self) -> AsyncRadarResource:
from .resources.radar import AsyncRadarResource
@@ -2167,6 +2181,12 @@ def vectorize(self) -> vectorize.VectorizeResourceWithRawResponse:
return VectorizeResourceWithRawResponse(self._client.vectorize)
+ @cached_property
+ def url_scanner(self) -> url_scanner.URLScannerResourceWithRawResponse:
+ from .resources.url_scanner import URLScannerResourceWithRawResponse
+
+ return URLScannerResourceWithRawResponse(self._client.url_scanner)
+
@cached_property
def radar(self) -> radar.RadarResourceWithRawResponse:
from .resources.radar import RadarResourceWithRawResponse
@@ -2686,6 +2706,12 @@ def vectorize(self) -> vectorize.AsyncVectorizeResourceWithRawResponse:
return AsyncVectorizeResourceWithRawResponse(self._client.vectorize)
+ @cached_property
+ def url_scanner(self) -> url_scanner.AsyncURLScannerResourceWithRawResponse:
+ from .resources.url_scanner import AsyncURLScannerResourceWithRawResponse
+
+ return AsyncURLScannerResourceWithRawResponse(self._client.url_scanner)
+
@cached_property
def radar(self) -> radar.AsyncRadarResourceWithRawResponse:
from .resources.radar import AsyncRadarResourceWithRawResponse
@@ -3205,6 +3231,12 @@ def vectorize(self) -> vectorize.VectorizeResourceWithStreamingResponse:
return VectorizeResourceWithStreamingResponse(self._client.vectorize)
+ @cached_property
+ def url_scanner(self) -> url_scanner.URLScannerResourceWithStreamingResponse:
+ from .resources.url_scanner import URLScannerResourceWithStreamingResponse
+
+ return URLScannerResourceWithStreamingResponse(self._client.url_scanner)
+
@cached_property
def radar(self) -> radar.RadarResourceWithStreamingResponse:
from .resources.radar import RadarResourceWithStreamingResponse
@@ -3728,6 +3760,12 @@ def vectorize(self) -> vectorize.AsyncVectorizeResourceWithStreamingResponse:
return AsyncVectorizeResourceWithStreamingResponse(self._client.vectorize)
+ @cached_property
+ def url_scanner(self) -> url_scanner.AsyncURLScannerResourceWithStreamingResponse:
+ from .resources.url_scanner import AsyncURLScannerResourceWithStreamingResponse
+
+ return AsyncURLScannerResourceWithStreamingResponse(self._client.url_scanner)
+
@cached_property
def radar(self) -> radar.AsyncRadarResourceWithStreamingResponse:
from .resources.radar import AsyncRadarResourceWithStreamingResponse
diff --git a/src/cloudflare/resources/url_scanner/__init__.py b/src/cloudflare/resources/url_scanner/__init__.py
new file mode 100644
index 00000000000..047e0b9adea
--- /dev/null
+++ b/src/cloudflare/resources/url_scanner/__init__.py
@@ -0,0 +1,47 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .scans import (
+ ScansResource,
+ AsyncScansResource,
+ ScansResourceWithRawResponse,
+ AsyncScansResourceWithRawResponse,
+ ScansResourceWithStreamingResponse,
+ AsyncScansResourceWithStreamingResponse,
+)
+from .responses import (
+ ResponsesResource,
+ AsyncResponsesResource,
+ ResponsesResourceWithRawResponse,
+ AsyncResponsesResourceWithRawResponse,
+ ResponsesResourceWithStreamingResponse,
+ AsyncResponsesResourceWithStreamingResponse,
+)
+from .url_scanner import (
+ URLScannerResource,
+ AsyncURLScannerResource,
+ URLScannerResourceWithRawResponse,
+ AsyncURLScannerResourceWithRawResponse,
+ URLScannerResourceWithStreamingResponse,
+ AsyncURLScannerResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "ResponsesResource",
+ "AsyncResponsesResource",
+ "ResponsesResourceWithRawResponse",
+ "AsyncResponsesResourceWithRawResponse",
+ "ResponsesResourceWithStreamingResponse",
+ "AsyncResponsesResourceWithStreamingResponse",
+ "ScansResource",
+ "AsyncScansResource",
+ "ScansResourceWithRawResponse",
+ "AsyncScansResourceWithRawResponse",
+ "ScansResourceWithStreamingResponse",
+ "AsyncScansResourceWithStreamingResponse",
+ "URLScannerResource",
+ "AsyncURLScannerResource",
+ "URLScannerResourceWithRawResponse",
+ "AsyncURLScannerResourceWithRawResponse",
+ "URLScannerResourceWithStreamingResponse",
+ "AsyncURLScannerResourceWithStreamingResponse",
+]
diff --git a/src/cloudflare/resources/url_scanner/responses.py b/src/cloudflare/resources/url_scanner/responses.py
new file mode 100644
index 00000000000..322e44344bc
--- /dev/null
+++ b/src/cloudflare/resources/url_scanner/responses.py
@@ -0,0 +1,182 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import httpx
+
+from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+
+__all__ = ["ResponsesResource", "AsyncResponsesResource"]
+
+
+class ResponsesResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ResponsesResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return the
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return ResponsesResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ResponsesResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return ResponsesResourceWithStreamingResponse(self)
+
+ def get(
+ self,
+ response_id: str,
+ *,
+ account_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> str:
+ """Returns the raw response of the network request.
+
+ If HTML, a plain text response
+ will be returned.
+
+ Args:
+ account_id: Account ID.
+
+ response_id: Response hash.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not response_id:
+ raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}")
+ extra_headers = {"Accept": "text/plain", **(extra_headers or {})}
+ return self._get(
+ f"/accounts/{account_id}/urlscanner/v2/responses/{response_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=str,
+ )
+
+
+class AsyncResponsesResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncResponsesResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return the
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncResponsesResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncResponsesResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return AsyncResponsesResourceWithStreamingResponse(self)
+
+ async def get(
+ self,
+ response_id: str,
+ *,
+ account_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> str:
+ """Returns the raw response of the network request.
+
+ If HTML, a plain text response
+ will be returned.
+
+ Args:
+ account_id: Account ID.
+
+ response_id: Response hash.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not response_id:
+ raise ValueError(f"Expected a non-empty value for `response_id` but received {response_id!r}")
+ extra_headers = {"Accept": "text/plain", **(extra_headers or {})}
+ return await self._get(
+ f"/accounts/{account_id}/urlscanner/v2/responses/{response_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=str,
+ )
+
+
+class ResponsesResourceWithRawResponse:
+ def __init__(self, responses: ResponsesResource) -> None:
+ self._responses = responses
+
+ self.get = to_raw_response_wrapper(
+ responses.get,
+ )
+
+
+class AsyncResponsesResourceWithRawResponse:
+ def __init__(self, responses: AsyncResponsesResource) -> None:
+ self._responses = responses
+
+ self.get = async_to_raw_response_wrapper(
+ responses.get,
+ )
+
+
+class ResponsesResourceWithStreamingResponse:
+ def __init__(self, responses: ResponsesResource) -> None:
+ self._responses = responses
+
+ self.get = to_streamed_response_wrapper(
+ responses.get,
+ )
+
+
+class AsyncResponsesResourceWithStreamingResponse:
+ def __init__(self, responses: AsyncResponsesResource) -> None:
+ self._responses = responses
+
+ self.get = async_to_streamed_response_wrapper(
+ responses.get,
+ )
diff --git a/src/cloudflare/resources/url_scanner/scans.py b/src/cloudflare/resources/url_scanner/scans.py
new file mode 100644
index 00000000000..dc22dbddda8
--- /dev/null
+++ b/src/cloudflare/resources/url_scanner/scans.py
@@ -0,0 +1,884 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, List, Type, Iterable, cast
+from typing_extensions import Literal
+
+import httpx
+
+from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ..._utils import (
+ maybe_transform,
+ async_maybe_transform,
+)
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ BinaryAPIResponse,
+ AsyncBinaryAPIResponse,
+ StreamedBinaryAPIResponse,
+ AsyncStreamedBinaryAPIResponse,
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ to_custom_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+ to_custom_streamed_response_wrapper,
+ async_to_custom_raw_response_wrapper,
+ async_to_custom_streamed_response_wrapper,
+)
+from ..._wrappers import ResultWrapper
+from ..._base_client import make_request_options
+from ...types.url_scanner import scan_list_params, scan_create_params, scan_screenshot_params, scan_bulk_create_params
+from ...types.url_scanner.scan_get_response import ScanGetResponse
+from ...types.url_scanner.scan_har_response import ScanHARResponse
+from ...types.url_scanner.scan_list_response import ScanListResponse
+from ...types.url_scanner.scan_create_response import ScanCreateResponse
+from ...types.url_scanner.scan_bulk_create_response import ScanBulkCreateResponse
+
+__all__ = ["ScansResource", "AsyncScansResource"]
+
+
+class ScansResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ScansResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return the
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return ScansResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ScansResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return ScansResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ account_id: str,
+ url: str,
+ customagent: str | NotGiven = NOT_GIVEN,
+ custom_headers: Dict[str, str] | NotGiven = NOT_GIVEN,
+ referer: str | NotGiven = NOT_GIVEN,
+ screenshots_resolutions: List[Literal["desktop", "mobile", "tablet"]] | NotGiven = NOT_GIVEN,
+ visibility: Literal["Public", "Unlisted"] | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> str:
+ """Submit a URL to scan.
+
+ Check limits at
+ https://developers.cloudflare.com/security-center/investigate/scan-limits/.
+
+ Args:
+ account_id: Account ID.
+
+ custom_headers: Set custom headers.
+
+ screenshots_resolutions: Take multiple screenshots targeting different device types.
+
+ visibility: The option `Public` means it will be included in listings like recent scans and
+ search results. `Unlisted` means it will not be included in the aforementioned
+ listings, users will need to have the scan's ID to access it. A a scan will be
+ automatically marked as unlisted if it fails, if it contains potential PII or
+ other sensitive material.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ return self._post(
+ f"/accounts/{account_id}/urlscanner/v2/scan",
+ body=maybe_transform(
+ {
+ "url": url,
+ "customagent": customagent,
+ "custom_headers": custom_headers,
+ "referer": referer,
+ "screenshots_resolutions": screenshots_resolutions,
+ "visibility": visibility,
+ },
+ scan_create_params.ScanCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[ScanCreateResponse]._unwrapper,
+ ),
+ cast_to=cast(Type[str], ResultWrapper[str]),
+ )
+
+ def list(
+ self,
+ *,
+ account_id: str,
+ q: str | NotGiven = NOT_GIVEN,
+ size: int | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> ScanListResponse:
+ """Use a subset of ElasticSearch Query syntax to filter scans.
+
+ Some example
+ queries:
- 'page.domain:microsoft AND verdicts.malicious:true AND NOT
+ page.domain:microsoft.com': malicious scans whose hostname starts with
+ "microsoft".
- 'apikey:me AND date:[2024-01 TO 2024-10]': my scans from 2024
+ January to 2024 October.
- 'page.domain:(blogspot OR www.blogspot)':
+ Searches for scans whose main domain starts with "blogspot" or with
+ "www.blogspot"
- 'date:>now-7d AND path:okta-sign-in.min.js: scans from the
+ last 7 days with any request path that ends with "okta-sign-in.min.js"
-
+ 'page.asn:AS24940 AND hash:xxx': Websites hosted in AS24940 where a resource
+ with the given hash was downloaded.
+
+ Args:
+ account_id: Account ID.
+
+ q: Filter scans
+
+ size: Limit the number of objects in the response.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ return self._get(
+ f"/accounts/{account_id}/urlscanner/v2/search",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "q": q,
+ "size": size,
+ },
+ scan_list_params.ScanListParams,
+ ),
+ ),
+ cast_to=ScanListResponse,
+ )
+
+ def bulk_create(
+ self,
+ *,
+ account_id: str,
+ body: Iterable[scan_bulk_create_params.Body],
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> ScanBulkCreateResponse:
+ """Submit URLs to scan.
+
+ Check limits at
+ https://developers.cloudflare.com/security-center/investigate/scan-limits/ and
+ take into account scans submitted in bulk have lower priority and may take
+ longer to finish.
+
+ Args:
+ account_id: Account ID.
+
+ body: List of urls to scan (up to a 100).
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ return self._post(
+ f"/accounts/{account_id}/urlscanner/v2/bulk",
+ body=maybe_transform(body, Iterable[scan_bulk_create_params.Body]),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ScanBulkCreateResponse,
+ )
+
+ def dom(
+ self,
+ scan_id: str,
+ *,
+ account_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> str:
+ """
+ Returns a plain text response, with the scan's DOM content as rendered by
+ Chrome.
+
+ Args:
+ account_id: Account ID.
+
+ scan_id: Scan UUID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not scan_id:
+ raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}")
+ extra_headers = {"Accept": "text/plain", **(extra_headers or {})}
+ return self._get(
+ f"/accounts/{account_id}/urlscanner/v2/dom/{scan_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=str,
+ )
+
+ def get(
+ self,
+ scan_id: str,
+ *,
+ account_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> ScanGetResponse:
+ """
+ Get URL scan by uuid
+
+ Args:
+ account_id: Account ID.
+
+ scan_id: Scan UUID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not scan_id:
+ raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}")
+ return self._get(
+ f"/accounts/{account_id}/urlscanner/v2/result/{scan_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ScanGetResponse,
+ )
+
+ def har(
+ self,
+ scan_id: str,
+ *,
+ account_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> ScanHARResponse:
+ """Get a URL scan's HAR file.
+
+ See HAR spec at
+ http://www.softwareishard.com/blog/har-12-spec/.
+
+ Args:
+ account_id: Account ID.
+
+ scan_id: Scan UUID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not scan_id:
+ raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}")
+ return self._get(
+ f"/accounts/{account_id}/urlscanner/v2/har/{scan_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ScanHARResponse,
+ )
+
+ def screenshot(
+ self,
+ scan_id: str,
+ *,
+ account_id: str,
+ resolution: Literal["desktop", "mobile", "tablet"] | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> BinaryAPIResponse:
+ """
+ Get scan's screenshot by resolution (desktop/mobile/tablet).
+
+ Args:
+ account_id: Account ID.
+
+ scan_id: Scan UUID.
+
+ resolution: Target device type.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not scan_id:
+ raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}")
+ extra_headers = {"Accept": "image/png", **(extra_headers or {})}
+ return self._get(
+ f"/accounts/{account_id}/urlscanner/v2/screenshots/{scan_id}.png",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"resolution": resolution}, scan_screenshot_params.ScanScreenshotParams),
+ ),
+ cast_to=BinaryAPIResponse,
+ )
+
+
+class AsyncScansResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncScansResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return the
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncScansResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncScansResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return AsyncScansResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ account_id: str,
+ url: str,
+ customagent: str | NotGiven = NOT_GIVEN,
+ custom_headers: Dict[str, str] | NotGiven = NOT_GIVEN,
+ referer: str | NotGiven = NOT_GIVEN,
+ screenshots_resolutions: List[Literal["desktop", "mobile", "tablet"]] | NotGiven = NOT_GIVEN,
+ visibility: Literal["Public", "Unlisted"] | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> str:
+ """Submit a URL to scan.
+
+ Check limits at
+ https://developers.cloudflare.com/security-center/investigate/scan-limits/.
+
+ Args:
+ account_id: Account ID.
+
+ custom_headers: Set custom headers.
+
+ screenshots_resolutions: Take multiple screenshots targeting different device types.
+
+ visibility: The option `Public` means it will be included in listings like recent scans and
+ search results. `Unlisted` means it will not be included in the aforementioned
+ listings, users will need to have the scan's ID to access it. A a scan will be
+ automatically marked as unlisted if it fails, if it contains potential PII or
+ other sensitive material.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ return await self._post(
+ f"/accounts/{account_id}/urlscanner/v2/scan",
+ body=await async_maybe_transform(
+ {
+ "url": url,
+ "customagent": customagent,
+ "custom_headers": custom_headers,
+ "referer": referer,
+ "screenshots_resolutions": screenshots_resolutions,
+ "visibility": visibility,
+ },
+ scan_create_params.ScanCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ post_parser=ResultWrapper[ScanCreateResponse]._unwrapper,
+ ),
+ cast_to=cast(Type[str], ResultWrapper[str]),
+ )
+
+ async def list(
+ self,
+ *,
+ account_id: str,
+ q: str | NotGiven = NOT_GIVEN,
+ size: int | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> ScanListResponse:
+ """Use a subset of ElasticSearch Query syntax to filter scans.
+
+ Some example
+ queries:
- 'page.domain:microsoft AND verdicts.malicious:true AND NOT
+ page.domain:microsoft.com': malicious scans whose hostname starts with
+ "microsoft".
- 'apikey:me AND date:[2024-01 TO 2024-10]': my scans from 2024
+ January to 2024 October.
- 'page.domain:(blogspot OR www.blogspot)':
+ Searches for scans whose main domain starts with "blogspot" or with
+ "www.blogspot"
- 'date:>now-7d AND path:okta-sign-in.min.js: scans from the
+ last 7 days with any request path that ends with "okta-sign-in.min.js"
-
+ 'page.asn:AS24940 AND hash:xxx': Websites hosted in AS24940 where a resource
+ with the given hash was downloaded.
+
+ Args:
+ account_id: Account ID.
+
+ q: Filter scans
+
+ size: Limit the number of objects in the response.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ return await self._get(
+ f"/accounts/{account_id}/urlscanner/v2/search",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {
+ "q": q,
+ "size": size,
+ },
+ scan_list_params.ScanListParams,
+ ),
+ ),
+ cast_to=ScanListResponse,
+ )
+
+ async def bulk_create(
+ self,
+ *,
+ account_id: str,
+ body: Iterable[scan_bulk_create_params.Body],
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> ScanBulkCreateResponse:
+ """Submit URLs to scan.
+
+ Check limits at
+ https://developers.cloudflare.com/security-center/investigate/scan-limits/ and
+ take into account scans submitted in bulk have lower priority and may take
+ longer to finish.
+
+ Args:
+ account_id: Account ID.
+
+ body: List of urls to scan (up to a 100).
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ return await self._post(
+ f"/accounts/{account_id}/urlscanner/v2/bulk",
+ body=await async_maybe_transform(body, Iterable[scan_bulk_create_params.Body]),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ScanBulkCreateResponse,
+ )
+
+ async def dom(
+ self,
+ scan_id: str,
+ *,
+ account_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> str:
+ """
+ Returns a plain text response, with the scan's DOM content as rendered by
+ Chrome.
+
+ Args:
+ account_id: Account ID.
+
+ scan_id: Scan UUID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not scan_id:
+ raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}")
+ extra_headers = {"Accept": "text/plain", **(extra_headers or {})}
+ return await self._get(
+ f"/accounts/{account_id}/urlscanner/v2/dom/{scan_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=str,
+ )
+
+ async def get(
+ self,
+ scan_id: str,
+ *,
+ account_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> ScanGetResponse:
+ """
+ Get URL scan by uuid
+
+ Args:
+ account_id: Account ID.
+
+ scan_id: Scan UUID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not scan_id:
+ raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}")
+ return await self._get(
+ f"/accounts/{account_id}/urlscanner/v2/result/{scan_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ScanGetResponse,
+ )
+
+ async def har(
+ self,
+ scan_id: str,
+ *,
+ account_id: str,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> ScanHARResponse:
+ """Get a URL scan's HAR file.
+
+ See HAR spec at
+ http://www.softwareishard.com/blog/har-12-spec/.
+
+ Args:
+ account_id: Account ID.
+
+ scan_id: Scan UUID.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not scan_id:
+ raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}")
+ return await self._get(
+ f"/accounts/{account_id}/urlscanner/v2/har/{scan_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ScanHARResponse,
+ )
+
+ async def screenshot(
+ self,
+ scan_id: str,
+ *,
+ account_id: str,
+ resolution: Literal["desktop", "mobile", "tablet"] | NotGiven = NOT_GIVEN,
+ # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
+ # The extra values given here take precedence over values defined on the client or passed to this method.
+ extra_headers: Headers | None = None,
+ extra_query: Query | None = None,
+ extra_body: Body | None = None,
+ timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ ) -> AsyncBinaryAPIResponse:
+ """
+ Get scan's screenshot by resolution (desktop/mobile/tablet).
+
+ Args:
+ account_id: Account ID.
+
+ scan_id: Scan UUID.
+
+ resolution: Target device type.
+
+ extra_headers: Send extra headers
+
+ extra_query: Add additional query parameters to the request
+
+ extra_body: Add additional JSON properties to the request
+
+ timeout: Override the client-level default timeout for this request, in seconds
+ """
+ if not account_id:
+ raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}")
+ if not scan_id:
+ raise ValueError(f"Expected a non-empty value for `scan_id` but received {scan_id!r}")
+ extra_headers = {"Accept": "image/png", **(extra_headers or {})}
+ return await self._get(
+ f"/accounts/{account_id}/urlscanner/v2/screenshots/{scan_id}.png",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"resolution": resolution}, scan_screenshot_params.ScanScreenshotParams
+ ),
+ ),
+ cast_to=AsyncBinaryAPIResponse,
+ )
+
+
+class ScansResourceWithRawResponse:
+ def __init__(self, scans: ScansResource) -> None:
+ self._scans = scans
+
+ self.create = to_raw_response_wrapper(
+ scans.create,
+ )
+ self.list = to_raw_response_wrapper(
+ scans.list,
+ )
+ self.bulk_create = to_raw_response_wrapper(
+ scans.bulk_create,
+ )
+ self.dom = to_raw_response_wrapper(
+ scans.dom,
+ )
+ self.get = to_raw_response_wrapper(
+ scans.get,
+ )
+ self.har = to_raw_response_wrapper(
+ scans.har,
+ )
+ self.screenshot = to_custom_raw_response_wrapper(
+ scans.screenshot,
+ BinaryAPIResponse,
+ )
+
+
+class AsyncScansResourceWithRawResponse:
+ def __init__(self, scans: AsyncScansResource) -> None:
+ self._scans = scans
+
+ self.create = async_to_raw_response_wrapper(
+ scans.create,
+ )
+ self.list = async_to_raw_response_wrapper(
+ scans.list,
+ )
+ self.bulk_create = async_to_raw_response_wrapper(
+ scans.bulk_create,
+ )
+ self.dom = async_to_raw_response_wrapper(
+ scans.dom,
+ )
+ self.get = async_to_raw_response_wrapper(
+ scans.get,
+ )
+ self.har = async_to_raw_response_wrapper(
+ scans.har,
+ )
+ self.screenshot = async_to_custom_raw_response_wrapper(
+ scans.screenshot,
+ AsyncBinaryAPIResponse,
+ )
+
+
+class ScansResourceWithStreamingResponse:
+ def __init__(self, scans: ScansResource) -> None:
+ self._scans = scans
+
+ self.create = to_streamed_response_wrapper(
+ scans.create,
+ )
+ self.list = to_streamed_response_wrapper(
+ scans.list,
+ )
+ self.bulk_create = to_streamed_response_wrapper(
+ scans.bulk_create,
+ )
+ self.dom = to_streamed_response_wrapper(
+ scans.dom,
+ )
+ self.get = to_streamed_response_wrapper(
+ scans.get,
+ )
+ self.har = to_streamed_response_wrapper(
+ scans.har,
+ )
+ self.screenshot = to_custom_streamed_response_wrapper(
+ scans.screenshot,
+ StreamedBinaryAPIResponse,
+ )
+
+
+class AsyncScansResourceWithStreamingResponse:
+ def __init__(self, scans: AsyncScansResource) -> None:
+ self._scans = scans
+
+ self.create = async_to_streamed_response_wrapper(
+ scans.create,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ scans.list,
+ )
+ self.bulk_create = async_to_streamed_response_wrapper(
+ scans.bulk_create,
+ )
+ self.dom = async_to_streamed_response_wrapper(
+ scans.dom,
+ )
+ self.get = async_to_streamed_response_wrapper(
+ scans.get,
+ )
+ self.har = async_to_streamed_response_wrapper(
+ scans.har,
+ )
+ self.screenshot = async_to_custom_streamed_response_wrapper(
+ scans.screenshot,
+ AsyncStreamedBinaryAPIResponse,
+ )
diff --git a/src/cloudflare/resources/url_scanner/url_scanner.py b/src/cloudflare/resources/url_scanner/url_scanner.py
new file mode 100644
index 00000000000..599de25507c
--- /dev/null
+++ b/src/cloudflare/resources/url_scanner/url_scanner.py
@@ -0,0 +1,134 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .scans import (
+ ScansResource,
+ AsyncScansResource,
+ ScansResourceWithRawResponse,
+ AsyncScansResourceWithRawResponse,
+ ScansResourceWithStreamingResponse,
+ AsyncScansResourceWithStreamingResponse,
+)
+from ..._compat import cached_property
+from .responses import (
+ ResponsesResource,
+ AsyncResponsesResource,
+ ResponsesResourceWithRawResponse,
+ AsyncResponsesResourceWithRawResponse,
+ ResponsesResourceWithStreamingResponse,
+ AsyncResponsesResourceWithStreamingResponse,
+)
+from ..._resource import SyncAPIResource, AsyncAPIResource
+
+__all__ = ["URLScannerResource", "AsyncURLScannerResource"]
+
+
+class URLScannerResource(SyncAPIResource):
+ @cached_property
+ def responses(self) -> ResponsesResource:
+ return ResponsesResource(self._client)
+
+ @cached_property
+ def scans(self) -> ScansResource:
+ return ScansResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> URLScannerResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return the
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return URLScannerResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> URLScannerResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return URLScannerResourceWithStreamingResponse(self)
+
+
+class AsyncURLScannerResource(AsyncAPIResource):
+ @cached_property
+ def responses(self) -> AsyncResponsesResource:
+ return AsyncResponsesResource(self._client)
+
+ @cached_property
+ def scans(self) -> AsyncScansResource:
+ return AsyncScansResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncURLScannerResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return the
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncURLScannerResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncURLScannerResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/cloudflare/cloudflare-python#with_streaming_response
+ """
+ return AsyncURLScannerResourceWithStreamingResponse(self)
+
+
+class URLScannerResourceWithRawResponse:
+ def __init__(self, url_scanner: URLScannerResource) -> None:
+ self._url_scanner = url_scanner
+
+ @cached_property
+ def responses(self) -> ResponsesResourceWithRawResponse:
+ return ResponsesResourceWithRawResponse(self._url_scanner.responses)
+
+ @cached_property
+ def scans(self) -> ScansResourceWithRawResponse:
+ return ScansResourceWithRawResponse(self._url_scanner.scans)
+
+
+class AsyncURLScannerResourceWithRawResponse:
+ def __init__(self, url_scanner: AsyncURLScannerResource) -> None:
+ self._url_scanner = url_scanner
+
+ @cached_property
+ def responses(self) -> AsyncResponsesResourceWithRawResponse:
+ return AsyncResponsesResourceWithRawResponse(self._url_scanner.responses)
+
+ @cached_property
+ def scans(self) -> AsyncScansResourceWithRawResponse:
+ return AsyncScansResourceWithRawResponse(self._url_scanner.scans)
+
+
+class URLScannerResourceWithStreamingResponse:
+ def __init__(self, url_scanner: URLScannerResource) -> None:
+ self._url_scanner = url_scanner
+
+ @cached_property
+ def responses(self) -> ResponsesResourceWithStreamingResponse:
+ return ResponsesResourceWithStreamingResponse(self._url_scanner.responses)
+
+ @cached_property
+ def scans(self) -> ScansResourceWithStreamingResponse:
+ return ScansResourceWithStreamingResponse(self._url_scanner.scans)
+
+
+class AsyncURLScannerResourceWithStreamingResponse:
+ def __init__(self, url_scanner: AsyncURLScannerResource) -> None:
+ self._url_scanner = url_scanner
+
+ @cached_property
+ def responses(self) -> AsyncResponsesResourceWithStreamingResponse:
+ return AsyncResponsesResourceWithStreamingResponse(self._url_scanner.responses)
+
+ @cached_property
+ def scans(self) -> AsyncScansResourceWithStreamingResponse:
+ return AsyncScansResourceWithStreamingResponse(self._url_scanner.scans)
diff --git a/src/cloudflare/types/url_scanner/__init__.py b/src/cloudflare/types/url_scanner/__init__.py
index f8ee8b14b1c..988295382a6 100644
--- a/src/cloudflare/types/url_scanner/__init__.py
+++ b/src/cloudflare/types/url_scanner/__init__.py
@@ -1,3 +1,15 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
from __future__ import annotations
+
+from .scan_list_params import ScanListParams as ScanListParams
+from .scan_dom_response import ScanDOMResponse as ScanDOMResponse
+from .scan_get_response import ScanGetResponse as ScanGetResponse
+from .scan_har_response import ScanHARResponse as ScanHARResponse
+from .scan_create_params import ScanCreateParams as ScanCreateParams
+from .scan_list_response import ScanListResponse as ScanListResponse
+from .scan_create_response import ScanCreateResponse as ScanCreateResponse
+from .response_get_response import ResponseGetResponse as ResponseGetResponse
+from .scan_screenshot_params import ScanScreenshotParams as ScanScreenshotParams
+from .scan_bulk_create_params import ScanBulkCreateParams as ScanBulkCreateParams
+from .scan_bulk_create_response import ScanBulkCreateResponse as ScanBulkCreateResponse
diff --git a/src/cloudflare/types/url_scanner/response_get_response.py b/src/cloudflare/types/url_scanner/response_get_response.py
new file mode 100644
index 00000000000..c2215a79037
--- /dev/null
+++ b/src/cloudflare/types/url_scanner/response_get_response.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import TypeAlias
+
+__all__ = ["ResponseGetResponse"]
+
+ResponseGetResponse: TypeAlias = str
diff --git a/src/cloudflare/types/url_scanner/scan_bulk_create_params.py b/src/cloudflare/types/url_scanner/scan_bulk_create_params.py
new file mode 100644
index 00000000000..6509c75550e
--- /dev/null
+++ b/src/cloudflare/types/url_scanner/scan_bulk_create_params.py
@@ -0,0 +1,43 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, List, Iterable
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["ScanBulkCreateParams", "Body"]
+
+
+class ScanBulkCreateParams(TypedDict, total=False):
+ account_id: Required[str]
+ """Account ID."""
+
+ body: Required[Iterable[Body]]
+ """List of urls to scan (up to a 100)."""
+
+
+class Body(TypedDict, total=False):
+ url: Required[str]
+
+ customagent: str
+
+ custom_headers: Annotated[Dict[str, str], PropertyInfo(alias="customHeaders")]
+ """Set custom headers."""
+
+ referer: str
+
+ screenshots_resolutions: Annotated[
+ List[Literal["desktop", "mobile", "tablet"]], PropertyInfo(alias="screenshotsResolutions")
+ ]
+ """Take multiple screenshots targeting different device types."""
+
+ visibility: Literal["Public", "Unlisted"]
+ """
+ The option `Public` means it will be included in listings like recent scans and
+ search results. `Unlisted` means it will not be included in the aforementioned
+ listings, users will need to have the scan's ID to access it. A a scan will be
+ automatically marked as unlisted if it fails, if it contains potential PII or
+ other sensitive material.
+ """
diff --git a/src/cloudflare/types/url_scanner/scan_bulk_create_response.py b/src/cloudflare/types/url_scanner/scan_bulk_create_response.py
new file mode 100644
index 00000000000..6a554a9878b
--- /dev/null
+++ b/src/cloudflare/types/url_scanner/scan_bulk_create_response.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from typing_extensions import TypeAlias
+
+from ..._models import BaseModel
+
+__all__ = ["ScanBulkCreateResponse", "ScanBulkCreateResponseItem", "ScanBulkCreateResponseItemOptions"]
+
+
+class ScanBulkCreateResponseItemOptions(BaseModel):
+ useragent: Optional[str] = None
+
+
+class ScanBulkCreateResponseItem(BaseModel):
+ api: str
+ """URL to api report."""
+
+ result: str
+ """URL to report."""
+
+ url: str
+ """Submitted URL"""
+
+ uuid: str
+ """Scan ID."""
+
+ visibility: str
+ """Submitted visibility status."""
+
+ options: Optional[ScanBulkCreateResponseItemOptions] = None
+
+
+ScanBulkCreateResponse: TypeAlias = List[ScanBulkCreateResponseItem]
diff --git a/src/cloudflare/types/url_scanner/scan_create_params.py b/src/cloudflare/types/url_scanner/scan_create_params.py
new file mode 100644
index 00000000000..782da4a9c85
--- /dev/null
+++ b/src/cloudflare/types/url_scanner/scan_create_params.py
@@ -0,0 +1,38 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, List
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from ..._utils import PropertyInfo
+
+__all__ = ["ScanCreateParams"]
+
+
+class ScanCreateParams(TypedDict, total=False):
+ account_id: Required[str]
+ """Account ID."""
+
+ url: Required[str]
+
+ customagent: str
+
+ custom_headers: Annotated[Dict[str, str], PropertyInfo(alias="customHeaders")]
+ """Set custom headers."""
+
+ referer: str
+
+ screenshots_resolutions: Annotated[
+ List[Literal["desktop", "mobile", "tablet"]], PropertyInfo(alias="screenshotsResolutions")
+ ]
+ """Take multiple screenshots targeting different device types."""
+
+ visibility: Literal["Public", "Unlisted"]
+ """
+ The option `Public` means it will be included in listings like recent scans and
+ search results. `Unlisted` means it will not be included in the aforementioned
+ listings, users will need to have the scan's ID to access it. A a scan will be
+ automatically marked as unlisted if it fails, if it contains potential PII or
+ other sensitive material.
+ """
diff --git a/src/cloudflare/types/url_scanner/scan_create_response.py b/src/cloudflare/types/url_scanner/scan_create_response.py
new file mode 100644
index 00000000000..4534ad1039a
--- /dev/null
+++ b/src/cloudflare/types/url_scanner/scan_create_response.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import TypeAlias
+
+__all__ = ["ScanCreateResponse"]
+
+ScanCreateResponse: TypeAlias = str
diff --git a/src/cloudflare/types/url_scanner/scan_dom_response.py b/src/cloudflare/types/url_scanner/scan_dom_response.py
new file mode 100644
index 00000000000..3d53cb81dbf
--- /dev/null
+++ b/src/cloudflare/types/url_scanner/scan_dom_response.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import TypeAlias
+
+__all__ = ["ScanDOMResponse"]
+
+ScanDOMResponse: TypeAlias = str
diff --git a/src/cloudflare/types/url_scanner/scan_get_response.py b/src/cloudflare/types/url_scanner/scan_get_response.py
new file mode 100644
index 00000000000..8d1ef4a39e0
--- /dev/null
+++ b/src/cloudflare/types/url_scanner/scan_get_response.py
@@ -0,0 +1,859 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "ScanGetResponse",
+ "Data",
+ "DataConsole",
+ "DataConsoleMessage",
+ "DataCookie",
+ "DataGlobal",
+ "DataLink",
+ "DataPerformance",
+ "DataRequest",
+ "DataRequestRequest",
+ "DataRequestRequestInitiator",
+ "DataRequestRequestRequest",
+ "DataRequestRequestRedirectResponse",
+ "DataRequestRequestRedirectResponseSecurityHeader",
+ "DataRequestResponse",
+ "DataRequestResponseASN",
+ "DataRequestResponseGeoip",
+ "DataRequestResponseResponse",
+ "DataRequestResponseResponseSecurityDetails",
+ "DataRequestResponseResponseSecurityHeader",
+ "Lists",
+ "ListsCertificate",
+ "Meta",
+ "MetaProcessors",
+ "MetaProcessorsASN",
+ "MetaProcessorsASNData",
+ "MetaProcessorsDNS",
+ "MetaProcessorsDNSData",
+ "MetaProcessorsDomainCategories",
+ "MetaProcessorsDomainCategoriesData",
+ "MetaProcessorsGeoip",
+ "MetaProcessorsGeoipData",
+ "MetaProcessorsGeoipDataGeoip",
+ "MetaProcessorsPhishing",
+ "MetaProcessorsRadarRank",
+ "MetaProcessorsRadarRankData",
+ "MetaProcessorsWappa",
+ "MetaProcessorsWappaData",
+ "MetaProcessorsWappaDataCategory",
+ "MetaProcessorsWappaDataConfidence",
+ "MetaProcessorsURLCategories",
+ "MetaProcessorsURLCategoriesData",
+ "MetaProcessorsURLCategoriesDataContent",
+ "MetaProcessorsURLCategoriesDataInherited",
+ "MetaProcessorsURLCategoriesDataInheritedContent",
+ "MetaProcessorsURLCategoriesDataInheritedRisk",
+ "MetaProcessorsURLCategoriesDataRisk",
+ "Page",
+ "PageScreenshot",
+ "Scanner",
+ "Stats",
+ "StatsDomainStat",
+ "StatsIPStat",
+ "StatsIPStatASN",
+ "StatsIPStatGeoip",
+ "StatsProtocolStat",
+ "StatsResourceStat",
+ "StatsServerStat",
+ "StatsTLSStat",
+ "StatsTLSStatProtocols",
+ "Task",
+ "TaskOptions",
+ "Verdicts",
+ "VerdictsOverall",
+]
+
+
+class DataConsoleMessage(BaseModel):
+ level: str
+
+ source: str
+
+ text: str
+
+ url: str
+
+
+class DataConsole(BaseModel):
+ message: DataConsoleMessage
+
+
+class DataCookie(BaseModel):
+ domain: str
+
+ expires: float
+
+ http_only: bool = FieldInfo(alias="httpOnly")
+
+ name: str
+
+ path: str
+
+ priority: str
+
+ same_party: bool = FieldInfo(alias="sameParty")
+
+ secure: bool
+
+ session: bool
+
+ size: float
+
+ source_port: float = FieldInfo(alias="sourcePort")
+
+ source_scheme: str = FieldInfo(alias="sourceScheme")
+
+ value: str
+
+
+class DataGlobal(BaseModel):
+ prop: str
+
+ type: str
+
+
+class DataLink(BaseModel):
+ href: str
+
+ text: str
+
+
+class DataPerformance(BaseModel):
+ duration: float
+
+ entry_type: str = FieldInfo(alias="entryType")
+
+ name: str
+
+ start_time: float = FieldInfo(alias="startTime")
+
+
+class DataRequestRequestInitiator(BaseModel):
+ host: str
+
+ type: str
+
+ url: str
+
+
+class DataRequestRequestRequest(BaseModel):
+ initial_priority: str = FieldInfo(alias="initialPriority")
+
+ is_same_site: bool = FieldInfo(alias="isSameSite")
+
+ method: str
+
+ mixed_content_type: str = FieldInfo(alias="mixedContentType")
+
+ referrer_policy: str = FieldInfo(alias="referrerPolicy")
+
+ url: str
+
+ headers: Optional[object] = None
+
+
+class DataRequestRequestRedirectResponseSecurityHeader(BaseModel):
+ name: str
+
+ value: str
+
+
+class DataRequestRequestRedirectResponse(BaseModel):
+ charset: str
+
+ mime_type: str = FieldInfo(alias="mimeType")
+
+ protocol: str
+
+ remote_ip_address: str = FieldInfo(alias="remoteIPAddress")
+
+ remote_port: float = FieldInfo(alias="remotePort")
+
+ security_headers: List[DataRequestRequestRedirectResponseSecurityHeader] = FieldInfo(alias="securityHeaders")
+
+ security_state: str = FieldInfo(alias="securityState")
+
+ status: float
+
+ status_text: str = FieldInfo(alias="statusText")
+
+ url: str
+
+ headers: Optional[object] = None
+
+
+class DataRequestRequest(BaseModel):
+ document_url: str = FieldInfo(alias="documentURL")
+
+ has_user_gesture: bool = FieldInfo(alias="hasUserGesture")
+
+ initiator: DataRequestRequestInitiator
+
+ redirect_has_extra_info: bool = FieldInfo(alias="redirectHasExtraInfo")
+
+ request: DataRequestRequestRequest
+
+ request_id: str = FieldInfo(alias="requestId")
+
+ type: str
+
+ wall_time: float = FieldInfo(alias="wallTime")
+
+ frame_id: Optional[str] = FieldInfo(alias="frameId", default=None)
+
+ loader_id: Optional[str] = FieldInfo(alias="loaderId", default=None)
+
+ primary_request: Optional[bool] = FieldInfo(alias="primaryRequest", default=None)
+
+ redirect_response: Optional[DataRequestRequestRedirectResponse] = FieldInfo(alias="redirectResponse", default=None)
+
+
+class DataRequestResponseASN(BaseModel):
+ asn: str
+
+ country: str
+
+ description: str
+
+ ip: str
+
+ name: str
+
+ org: str
+
+
+class DataRequestResponseGeoip(BaseModel):
+ city: str
+
+ country: str
+
+ country_name: str
+
+ geoname_id: str = FieldInfo(alias="geonameId")
+
+ ll: List[object]
+
+ region: str
+
+
+class DataRequestResponseResponseSecurityDetails(BaseModel):
+ certificate_id: float = FieldInfo(alias="certificateId")
+
+ certificate_transparency_compliance: str = FieldInfo(alias="certificateTransparencyCompliance")
+
+ cipher: str
+
+ encrypted_client_hello: bool = FieldInfo(alias="encryptedClientHello")
+
+ issuer: str
+
+ key_exchange: str = FieldInfo(alias="keyExchange")
+
+ key_exchange_group: str = FieldInfo(alias="keyExchangeGroup")
+
+ protocol: str
+
+ san_list: List[str] = FieldInfo(alias="sanList")
+
+ server_signature_algorithm: float = FieldInfo(alias="serverSignatureAlgorithm")
+
+ subject_name: str = FieldInfo(alias="subjectName")
+
+ valid_from: float = FieldInfo(alias="validFrom")
+
+ valid_to: float = FieldInfo(alias="validTo")
+
+
+class DataRequestResponseResponseSecurityHeader(BaseModel):
+ name: str
+
+ value: str
+
+
+class DataRequestResponseResponse(BaseModel):
+ charset: str
+
+ mime_type: str = FieldInfo(alias="mimeType")
+
+ protocol: str
+
+ remote_ip_address: str = FieldInfo(alias="remoteIPAddress")
+
+ remote_port: float = FieldInfo(alias="remotePort")
+
+ security_details: DataRequestResponseResponseSecurityDetails = FieldInfo(alias="securityDetails")
+
+ security_headers: List[DataRequestResponseResponseSecurityHeader] = FieldInfo(alias="securityHeaders")
+
+ security_state: str = FieldInfo(alias="securityState")
+
+ status: float
+
+ status_text: str = FieldInfo(alias="statusText")
+
+ url: str
+
+ headers: Optional[object] = None
+
+
+class DataRequestResponse(BaseModel):
+ asn: DataRequestResponseASN
+
+ data_length: float = FieldInfo(alias="dataLength")
+
+ encoded_data_length: float = FieldInfo(alias="encodedDataLength")
+
+ geoip: DataRequestResponseGeoip
+
+ has_extra_info: bool = FieldInfo(alias="hasExtraInfo")
+
+ request_id: str = FieldInfo(alias="requestId")
+
+ response: DataRequestResponseResponse
+
+ size: float
+
+ type: str
+
+ content_available: Optional[bool] = FieldInfo(alias="contentAvailable", default=None)
+
+ hash: Optional[str] = None
+
+
+class DataRequest(BaseModel):
+ request: DataRequestRequest
+
+ response: DataRequestResponse
+
+ requests: Optional[List[DataRequestRequest]] = None
+
+
+class Data(BaseModel):
+ console: List[DataConsole]
+
+ cookies: List[DataCookie]
+
+ globals: List[DataGlobal]
+
+ links: List[DataLink]
+
+ performance: List[DataPerformance]
+
+ requests: List[DataRequest]
+
+
+class ListsCertificate(BaseModel):
+ issuer: str
+
+ subject_name: str = FieldInfo(alias="subjectName")
+
+ valid_from: float = FieldInfo(alias="validFrom")
+
+ valid_to: float = FieldInfo(alias="validTo")
+
+
+class Lists(BaseModel):
+ asns: List[str]
+
+ certificates: List[ListsCertificate]
+
+ continents: List[str]
+
+ countries: List[str]
+
+ domains: List[str]
+
+ hashes: List[str]
+
+ ips: List[str]
+
+ link_domains: List[str] = FieldInfo(alias="linkDomains")
+
+ servers: List[str]
+
+ urls: List[str]
+
+
+class MetaProcessorsASNData(BaseModel):
+ asn: str
+
+ country: str
+
+ description: str
+
+ ip: str
+
+ name: str
+
+
+class MetaProcessorsASN(BaseModel):
+ data: List[MetaProcessorsASNData]
+
+
+class MetaProcessorsDNSData(BaseModel):
+ address: str
+
+ dnssec_valid: bool
+
+ name: str
+
+ type: str
+
+
+class MetaProcessorsDNS(BaseModel):
+ data: List[MetaProcessorsDNSData]
+
+
+class MetaProcessorsDomainCategoriesData(BaseModel):
+ inherited: object
+
+ is_primary: bool = FieldInfo(alias="isPrimary")
+
+ name: str
+
+
+class MetaProcessorsDomainCategories(BaseModel):
+ data: List[MetaProcessorsDomainCategoriesData]
+
+
+class MetaProcessorsGeoipDataGeoip(BaseModel):
+ city: str
+
+ country: str
+
+ country_name: str
+
+ ll: List[float]
+
+ region: str
+
+
+class MetaProcessorsGeoipData(BaseModel):
+ geoip: MetaProcessorsGeoipDataGeoip
+
+ ip: str
+
+
+class MetaProcessorsGeoip(BaseModel):
+ data: List[MetaProcessorsGeoipData]
+
+
+class MetaProcessorsPhishing(BaseModel):
+ data: List[str]
+
+
+class MetaProcessorsRadarRankData(BaseModel):
+ bucket: str
+
+ hostname: str
+
+ rank: Optional[float] = None
+
+
+class MetaProcessorsRadarRank(BaseModel):
+ data: List[MetaProcessorsRadarRankData]
+
+
+class MetaProcessorsWappaDataCategory(BaseModel):
+ name: str
+
+ priority: float
+
+
+class MetaProcessorsWappaDataConfidence(BaseModel):
+ confidence: float
+
+ name: str
+
+ pattern: str
+
+ pattern_type: str = FieldInfo(alias="patternType")
+
+
+class MetaProcessorsWappaData(BaseModel):
+ app: str
+
+ categories: List[MetaProcessorsWappaDataCategory]
+
+ confidence: List[MetaProcessorsWappaDataConfidence]
+
+ confidence_total: float = FieldInfo(alias="confidenceTotal")
+
+ icon: str
+
+ website: str
+
+
+class MetaProcessorsWappa(BaseModel):
+ data: List[MetaProcessorsWappaData]
+
+
+class MetaProcessorsURLCategoriesDataContent(BaseModel):
+ id: float
+
+ name: str
+
+ super_category_id: float
+
+
+class MetaProcessorsURLCategoriesDataInheritedContent(BaseModel):
+ id: float
+
+ name: str
+
+ super_category_id: float
+
+
+class MetaProcessorsURLCategoriesDataInheritedRisk(BaseModel):
+ id: float
+
+ name: str
+
+ super_category_id: float
+
+
+class MetaProcessorsURLCategoriesDataInherited(BaseModel):
+ content: List[MetaProcessorsURLCategoriesDataInheritedContent]
+
+ from_: str = FieldInfo(alias="from")
+
+ risks: List[MetaProcessorsURLCategoriesDataInheritedRisk]
+
+
+class MetaProcessorsURLCategoriesDataRisk(BaseModel):
+ id: float
+
+ name: str
+
+ super_category_id: float
+
+
+class MetaProcessorsURLCategoriesData(BaseModel):
+ content: List[MetaProcessorsURLCategoriesDataContent]
+
+ inherited: MetaProcessorsURLCategoriesDataInherited
+
+ name: str
+
+ risks: List[MetaProcessorsURLCategoriesDataRisk]
+
+
+class MetaProcessorsURLCategories(BaseModel):
+ data: List[MetaProcessorsURLCategoriesData]
+
+
+class MetaProcessors(BaseModel):
+ asn: MetaProcessorsASN
+
+ dns: MetaProcessorsDNS
+
+ domain_categories: MetaProcessorsDomainCategories = FieldInfo(alias="domainCategories")
+
+ geoip: MetaProcessorsGeoip
+
+ phishing: MetaProcessorsPhishing
+
+ radar_rank: MetaProcessorsRadarRank = FieldInfo(alias="radarRank")
+
+ wappa: MetaProcessorsWappa
+
+ url_categories: Optional[MetaProcessorsURLCategories] = FieldInfo(alias="urlCategories", default=None)
+
+
+class Meta(BaseModel):
+ processors: MetaProcessors
+
+
+class PageScreenshot(BaseModel):
+ dhash: str
+
+ mm3_hash: float = FieldInfo(alias="mm3Hash")
+
+ name: str
+
+ phash: str
+
+
+class Page(BaseModel):
+ apex_domain: str = FieldInfo(alias="apexDomain")
+
+ asn: str
+
+ asnname: str
+
+ city: str
+
+ country: str
+
+ domain: str
+
+ ip: str
+
+ mime_type: str = FieldInfo(alias="mimeType")
+
+ server: str
+
+ status: str
+
+ title: str
+
+ tls_age_days: float = FieldInfo(alias="tlsAgeDays")
+
+ tls_issuer: str = FieldInfo(alias="tlsIssuer")
+
+ tls_valid_days: float = FieldInfo(alias="tlsValidDays")
+
+ tls_valid_from: str = FieldInfo(alias="tlsValidFrom")
+
+ url: str
+
+ screenshot: Optional[PageScreenshot] = None
+
+
+class Scanner(BaseModel):
+ colo: str
+
+ country: str
+
+
+class StatsDomainStat(BaseModel):
+ count: float
+
+ countries: List[str]
+
+ domain: str
+
+ encoded_size: float = FieldInfo(alias="encodedSize")
+
+ index: float
+
+ initiators: List[str]
+
+ ips: List[str]
+
+ redirects: float
+
+ size: float
+
+
+class StatsIPStatASN(BaseModel):
+ asn: str
+
+ country: str
+
+ description: str
+
+ ip: str
+
+ name: str
+
+ org: str
+
+
+class StatsIPStatGeoip(BaseModel):
+ city: str
+
+ country: str
+
+ country_name: str
+
+ ll: List[float]
+
+ region: str
+
+
+class StatsIPStat(BaseModel):
+ asn: StatsIPStatASN
+
+ countries: List[str]
+
+ domains: List[str]
+
+ encoded_size: float = FieldInfo(alias="encodedSize")
+
+ geoip: StatsIPStatGeoip
+
+ index: float
+
+ ip: str
+
+ ipv6: bool
+
+ redirects: float
+
+ requests: float
+
+ size: float
+
+ count: Optional[float] = None
+
+
+class StatsProtocolStat(BaseModel):
+ count: float
+
+ countries: List[str]
+
+ encoded_size: float = FieldInfo(alias="encodedSize")
+
+ ips: List[str]
+
+ protocol: str
+
+ size: float
+
+
+class StatsResourceStat(BaseModel):
+ compression: float
+
+ count: float
+
+ countries: List[str]
+
+ encoded_size: float = FieldInfo(alias="encodedSize")
+
+ ips: List[str]
+
+ percentage: float
+
+ size: float
+
+ type: str
+
+
+class StatsServerStat(BaseModel):
+ count: float
+
+ countries: List[str]
+
+ encoded_size: float = FieldInfo(alias="encodedSize")
+
+ ips: List[str]
+
+ server: str
+
+ size: float
+
+
+class StatsTLSStatProtocols(BaseModel):
+ tls_1_3_aes_128_gcm: float = FieldInfo(alias="TLS 1.3 / AES_128_GCM")
+
+
+class StatsTLSStat(BaseModel):
+ count: float
+
+ countries: List[str]
+
+ encoded_size: float = FieldInfo(alias="encodedSize")
+
+ ips: List[str]
+
+ protocols: StatsTLSStatProtocols
+
+ security_state: str = FieldInfo(alias="securityState")
+
+ size: float
+
+
+class Stats(BaseModel):
+ domain_stats: List[StatsDomainStat] = FieldInfo(alias="domainStats")
+
+ ip_stats: List[StatsIPStat] = FieldInfo(alias="ipStats")
+
+ i_pv6_percentage: float = FieldInfo(alias="IPv6Percentage")
+
+ malicious: float
+
+ protocol_stats: List[StatsProtocolStat] = FieldInfo(alias="protocolStats")
+
+ resource_stats: List[StatsResourceStat] = FieldInfo(alias="resourceStats")
+
+ secure_percentage: float = FieldInfo(alias="securePercentage")
+
+ secure_requests: float = FieldInfo(alias="secureRequests")
+
+ server_stats: List[StatsServerStat] = FieldInfo(alias="serverStats")
+
+ tls_stats: List[StatsTLSStat] = FieldInfo(alias="tlsStats")
+
+ total_links: float = FieldInfo(alias="totalLinks")
+
+ uniq_asns: float = FieldInfo(alias="uniqASNs")
+
+ uniq_countries: float = FieldInfo(alias="uniqCountries")
+
+
+class TaskOptions(BaseModel):
+ custom_headers: Optional[object] = FieldInfo(alias="customHeaders", default=None)
+ """Custom headers set."""
+
+ screenshots_resolutions: Optional[List[str]] = FieldInfo(alias="screenshotsResolutions", default=None)
+
+
+class Task(BaseModel):
+ apex_domain: str = FieldInfo(alias="apexDomain")
+
+ domain: str
+
+ dom_url: str = FieldInfo(alias="domURL")
+
+ method: str
+
+ options: TaskOptions
+
+ report_url: str = FieldInfo(alias="reportURL")
+
+ screenshot_url: str = FieldInfo(alias="screenshotURL")
+
+ source: str
+
+ success: bool
+
+ time: str
+
+ url: str
+
+ uuid: str
+
+ visibility: str
+
+
+class VerdictsOverall(BaseModel):
+ categories: List[str]
+
+ has_verdicts: bool = FieldInfo(alias="hasVerdicts")
+
+ malicious: bool
+
+ tags: List[str]
+
+
+class Verdicts(BaseModel):
+ overall: VerdictsOverall
+
+
+class ScanGetResponse(BaseModel):
+ data: Data
+
+ lists: Lists
+
+ meta: Meta
+
+ page: Page
+
+ scanner: Scanner
+
+ stats: Stats
+
+ task: Task
+
+ verdicts: Verdicts
diff --git a/src/cloudflare/types/url_scanner/scan_har_response.py b/src/cloudflare/types/url_scanner/scan_har_response.py
new file mode 100644
index 00000000000..2a4f344de77
--- /dev/null
+++ b/src/cloudflare/types/url_scanner/scan_har_response.py
@@ -0,0 +1,143 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = [
+ "ScanHARResponse",
+ "Log",
+ "LogCreator",
+ "LogEntry",
+ "LogEntryRequest",
+ "LogEntryRequestHeader",
+ "LogEntryResponse",
+ "LogEntryResponseContent",
+ "LogEntryResponseHeader",
+ "LogPage",
+ "LogPagePageTimings",
+]
+
+
+class LogCreator(BaseModel):
+ comment: str
+
+ name: str
+
+ version: str
+
+
+class LogEntryRequestHeader(BaseModel):
+ name: str
+
+ value: str
+
+
+class LogEntryRequest(BaseModel):
+ body_size: float = FieldInfo(alias="bodySize")
+
+ headers: List[LogEntryRequestHeader]
+
+ headers_size: float = FieldInfo(alias="headersSize")
+
+ http_version: str = FieldInfo(alias="httpVersion")
+
+ method: str
+
+ url: str
+
+
+class LogEntryResponseContent(BaseModel):
+ mime_type: str = FieldInfo(alias="mimeType")
+
+ size: float
+
+ compression: Optional[int] = None
+
+
+class LogEntryResponseHeader(BaseModel):
+ name: str
+
+ value: str
+
+
+class LogEntryResponse(BaseModel):
+ transfer_size: float = FieldInfo(alias="_transferSize")
+
+ body_size: float = FieldInfo(alias="bodySize")
+
+ content: LogEntryResponseContent
+
+ headers: List[LogEntryResponseHeader]
+
+ headers_size: float = FieldInfo(alias="headersSize")
+
+ http_version: str = FieldInfo(alias="httpVersion")
+
+ redirect_url: str = FieldInfo(alias="redirectURL")
+
+ status: float
+
+ status_text: str = FieldInfo(alias="statusText")
+
+
+class LogEntry(BaseModel):
+ initial_priority: str = FieldInfo(alias="_initialPriority")
+
+ initiator_type: str = FieldInfo(alias="_initiator_type")
+
+ priority: str = FieldInfo(alias="_priority")
+
+ request_id: str = FieldInfo(alias="_requestId")
+
+ request_time: float = FieldInfo(alias="_requestTime")
+
+ resource_type: str = FieldInfo(alias="_resourceType")
+
+ cache: object
+
+ connection: str
+
+ pageref: str
+
+ request: LogEntryRequest
+
+ response: LogEntryResponse
+
+ server_ip_address: str = FieldInfo(alias="serverIPAddress")
+
+ started_date_time: str = FieldInfo(alias="startedDateTime")
+
+ time: float
+
+
+class LogPagePageTimings(BaseModel):
+ on_content_load: float = FieldInfo(alias="onContentLoad")
+
+ on_load: float = FieldInfo(alias="onLoad")
+
+
+class LogPage(BaseModel):
+ id: str
+
+ page_timings: LogPagePageTimings = FieldInfo(alias="pageTimings")
+
+ started_date_time: str = FieldInfo(alias="startedDateTime")
+
+ title: str
+
+
+class Log(BaseModel):
+ creator: LogCreator
+
+ entries: List[LogEntry]
+
+ pages: List[LogPage]
+
+ version: str
+
+
+class ScanHARResponse(BaseModel):
+ log: Log
diff --git a/src/cloudflare/types/url_scanner/scan_list_params.py b/src/cloudflare/types/url_scanner/scan_list_params.py
new file mode 100644
index 00000000000..97421b92ccc
--- /dev/null
+++ b/src/cloudflare/types/url_scanner/scan_list_params.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["ScanListParams"]
+
+
+class ScanListParams(TypedDict, total=False):
+ account_id: Required[str]
+ """Account ID."""
+
+ q: str
+ """Filter scans"""
+
+ size: int
+ """Limit the number of objects in the response."""
diff --git a/src/cloudflare/types/url_scanner/scan_list_response.py b/src/cloudflare/types/url_scanner/scan_list_response.py
new file mode 100644
index 00000000000..7cb1be617a4
--- /dev/null
+++ b/src/cloudflare/types/url_scanner/scan_list_response.py
@@ -0,0 +1,61 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from pydantic import Field as FieldInfo
+
+from ..._models import BaseModel
+
+__all__ = ["ScanListResponse", "Result", "ResultPage", "ResultStats", "ResultTask", "ResultVerdicts"]
+
+
+class ResultPage(BaseModel):
+ asn: str
+
+ country: str
+
+ ip: str
+
+ url: str
+
+
+class ResultStats(BaseModel):
+ data_length: float = FieldInfo(alias="dataLength")
+
+ requests: float
+
+ uniq_countries: float = FieldInfo(alias="uniqCountries")
+
+ uniq_ips: float = FieldInfo(alias="uniqIPs")
+
+
+class ResultTask(BaseModel):
+ time: str
+
+ url: str
+
+ uuid: str
+
+ visibility: str
+
+
+class ResultVerdicts(BaseModel):
+ malicious: bool
+
+
+class Result(BaseModel):
+ api_id: str = FieldInfo(alias="_id")
+
+ page: ResultPage
+
+ result: str
+
+ stats: ResultStats
+
+ task: ResultTask
+
+ verdicts: ResultVerdicts
+
+
+class ScanListResponse(BaseModel):
+ results: List[Result]
diff --git a/src/cloudflare/types/url_scanner/scan_screenshot_params.py b/src/cloudflare/types/url_scanner/scan_screenshot_params.py
new file mode 100644
index 00000000000..263a003363f
--- /dev/null
+++ b/src/cloudflare/types/url_scanner/scan_screenshot_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ScanScreenshotParams"]
+
+
+class ScanScreenshotParams(TypedDict, total=False):
+ account_id: Required[str]
+ """Account ID."""
+
+ resolution: Literal["desktop", "mobile", "tablet"]
+ """Target device type."""
diff --git a/tests/api_resources/url_scanner/__init__.py b/tests/api_resources/url_scanner/__init__.py
new file mode 100644
index 00000000000..fd8019a9a1a
--- /dev/null
+++ b/tests/api_resources/url_scanner/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/url_scanner/test_responses.py b/tests/api_resources/url_scanner/test_responses.py
new file mode 100644
index 00000000000..1690ba966df
--- /dev/null
+++ b/tests/api_resources/url_scanner/test_responses.py
@@ -0,0 +1,117 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from cloudflare import Cloudflare, AsyncCloudflare
+from tests.utils import assert_matches_type
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestResponses:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_get(self, client: Cloudflare) -> None:
+ response = client.url_scanner.responses.get(
+ response_id="response_id",
+ account_id="account_id",
+ )
+ assert_matches_type(str, response, path=["response"])
+
+ @parametrize
+ def test_raw_response_get(self, client: Cloudflare) -> None:
+ http_response = client.url_scanner.responses.with_raw_response.get(
+ response_id="response_id",
+ account_id="account_id",
+ )
+
+ assert http_response.is_closed is True
+ assert http_response.http_request.headers.get("X-Stainless-Lang") == "python"
+ response = http_response.parse()
+ assert_matches_type(str, response, path=["response"])
+
+ @parametrize
+ def test_streaming_response_get(self, client: Cloudflare) -> None:
+ with client.url_scanner.responses.with_streaming_response.get(
+ response_id="response_id",
+ account_id="account_id",
+ ) as http_response:
+ assert not http_response.is_closed
+ assert http_response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ response = http_response.parse()
+ assert_matches_type(str, response, path=["response"])
+
+ assert cast(Any, http_response.is_closed) is True
+
+ @parametrize
+ def test_path_params_get(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.url_scanner.responses.with_raw_response.get(
+ response_id="response_id",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"):
+ client.url_scanner.responses.with_raw_response.get(
+ response_id="",
+ account_id="account_id",
+ )
+
+
+class TestAsyncResponses:
+ parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ async def test_method_get(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.url_scanner.responses.get(
+ response_id="response_id",
+ account_id="account_id",
+ )
+ assert_matches_type(str, response, path=["response"])
+
+ @parametrize
+ async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None:
+ http_response = await async_client.url_scanner.responses.with_raw_response.get(
+ response_id="response_id",
+ account_id="account_id",
+ )
+
+ assert http_response.is_closed is True
+ assert http_response.http_request.headers.get("X-Stainless-Lang") == "python"
+ response = await http_response.parse()
+ assert_matches_type(str, response, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.url_scanner.responses.with_streaming_response.get(
+ response_id="response_id",
+ account_id="account_id",
+ ) as http_response:
+ assert not http_response.is_closed
+ assert http_response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ response = await http_response.parse()
+ assert_matches_type(str, response, path=["response"])
+
+ assert cast(Any, http_response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_get(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.url_scanner.responses.with_raw_response.get(
+ response_id="response_id",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `response_id` but received ''"):
+ await async_client.url_scanner.responses.with_raw_response.get(
+ response_id="",
+ account_id="account_id",
+ )
diff --git a/tests/api_resources/url_scanner/test_scans.py b/tests/api_resources/url_scanner/test_scans.py
new file mode 100644
index 00000000000..933d352b902
--- /dev/null
+++ b/tests/api_resources/url_scanner/test_scans.py
@@ -0,0 +1,777 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import httpx
+import pytest
+from respx import MockRouter
+
+from cloudflare import Cloudflare, AsyncCloudflare
+from tests.utils import assert_matches_type
+from cloudflare._response import (
+ BinaryAPIResponse,
+ AsyncBinaryAPIResponse,
+ StreamedBinaryAPIResponse,
+ AsyncStreamedBinaryAPIResponse,
+)
+from cloudflare.types.url_scanner import (
+ ScanGetResponse,
+ ScanHARResponse,
+ ScanListResponse,
+ ScanBulkCreateResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestScans:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: Cloudflare) -> None:
+ scan = client.url_scanner.scans.create(
+ account_id="account_id",
+ url="https://www.example.com",
+ )
+ assert_matches_type(str, scan, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: Cloudflare) -> None:
+ scan = client.url_scanner.scans.create(
+ account_id="account_id",
+ url="https://www.example.com",
+ customagent="customagent",
+ custom_headers={"foo": "string"},
+ referer="referer",
+ screenshots_resolutions=["desktop"],
+ visibility="Public",
+ )
+ assert_matches_type(str, scan, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: Cloudflare) -> None:
+ response = client.url_scanner.scans.with_raw_response.create(
+ account_id="account_id",
+ url="https://www.example.com",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ scan = response.parse()
+ assert_matches_type(str, scan, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: Cloudflare) -> None:
+ with client.url_scanner.scans.with_streaming_response.create(
+ account_id="account_id",
+ url="https://www.example.com",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ scan = response.parse()
+ assert_matches_type(str, scan, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.url_scanner.scans.with_raw_response.create(
+ account_id="",
+ url="https://www.example.com",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Cloudflare) -> None:
+ scan = client.url_scanner.scans.list(
+ account_id="account_id",
+ )
+ assert_matches_type(ScanListResponse, scan, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Cloudflare) -> None:
+ scan = client.url_scanner.scans.list(
+ account_id="account_id",
+ q="q",
+ size=100,
+ )
+ assert_matches_type(ScanListResponse, scan, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Cloudflare) -> None:
+ response = client.url_scanner.scans.with_raw_response.list(
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ scan = response.parse()
+ assert_matches_type(ScanListResponse, scan, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Cloudflare) -> None:
+ with client.url_scanner.scans.with_streaming_response.list(
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ scan = response.parse()
+ assert_matches_type(ScanListResponse, scan, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_list(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.url_scanner.scans.with_raw_response.list(
+ account_id="",
+ )
+
+ @parametrize
+ def test_method_bulk_create(self, client: Cloudflare) -> None:
+ scan = client.url_scanner.scans.bulk_create(
+ account_id="account_id",
+ body=[{"url": "https://www.example.com"}],
+ )
+ assert_matches_type(ScanBulkCreateResponse, scan, path=["response"])
+
+ @parametrize
+ def test_raw_response_bulk_create(self, client: Cloudflare) -> None:
+ response = client.url_scanner.scans.with_raw_response.bulk_create(
+ account_id="account_id",
+ body=[{"url": "https://www.example.com"}],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ scan = response.parse()
+ assert_matches_type(ScanBulkCreateResponse, scan, path=["response"])
+
+ @parametrize
+ def test_streaming_response_bulk_create(self, client: Cloudflare) -> None:
+ with client.url_scanner.scans.with_streaming_response.bulk_create(
+ account_id="account_id",
+ body=[{"url": "https://www.example.com"}],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ scan = response.parse()
+ assert_matches_type(ScanBulkCreateResponse, scan, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_bulk_create(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.url_scanner.scans.with_raw_response.bulk_create(
+ account_id="",
+ body=[{"url": "https://www.example.com"}],
+ )
+
+ @parametrize
+ def test_method_dom(self, client: Cloudflare) -> None:
+ scan = client.url_scanner.scans.dom(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+ assert_matches_type(str, scan, path=["response"])
+
+ @parametrize
+ def test_raw_response_dom(self, client: Cloudflare) -> None:
+ response = client.url_scanner.scans.with_raw_response.dom(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ scan = response.parse()
+ assert_matches_type(str, scan, path=["response"])
+
+ @parametrize
+ def test_streaming_response_dom(self, client: Cloudflare) -> None:
+ with client.url_scanner.scans.with_streaming_response.dom(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ scan = response.parse()
+ assert_matches_type(str, scan, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_dom(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.url_scanner.scans.with_raw_response.dom(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"):
+ client.url_scanner.scans.with_raw_response.dom(
+ scan_id="",
+ account_id="account_id",
+ )
+
+ @parametrize
+ def test_method_get(self, client: Cloudflare) -> None:
+ scan = client.url_scanner.scans.get(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+ assert_matches_type(ScanGetResponse, scan, path=["response"])
+
+ @parametrize
+ def test_raw_response_get(self, client: Cloudflare) -> None:
+ response = client.url_scanner.scans.with_raw_response.get(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ scan = response.parse()
+ assert_matches_type(ScanGetResponse, scan, path=["response"])
+
+ @parametrize
+ def test_streaming_response_get(self, client: Cloudflare) -> None:
+ with client.url_scanner.scans.with_streaming_response.get(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ scan = response.parse()
+ assert_matches_type(ScanGetResponse, scan, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_get(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.url_scanner.scans.with_raw_response.get(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"):
+ client.url_scanner.scans.with_raw_response.get(
+ scan_id="",
+ account_id="account_id",
+ )
+
+ @parametrize
+ def test_method_har(self, client: Cloudflare) -> None:
+ scan = client.url_scanner.scans.har(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+ assert_matches_type(ScanHARResponse, scan, path=["response"])
+
+ @parametrize
+ def test_raw_response_har(self, client: Cloudflare) -> None:
+ response = client.url_scanner.scans.with_raw_response.har(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ scan = response.parse()
+ assert_matches_type(ScanHARResponse, scan, path=["response"])
+
+ @parametrize
+ def test_streaming_response_har(self, client: Cloudflare) -> None:
+ with client.url_scanner.scans.with_streaming_response.har(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ scan = response.parse()
+ assert_matches_type(ScanHARResponse, scan, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_har(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.url_scanner.scans.with_raw_response.har(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"):
+ client.url_scanner.scans.with_raw_response.har(
+ scan_id="",
+ account_id="account_id",
+ )
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_method_screenshot(self, client: Cloudflare, respx_mock: MockRouter) -> None:
+ respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock(
+ return_value=httpx.Response(200, json={"foo": "bar"})
+ )
+ scan = client.url_scanner.scans.screenshot(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+ assert scan.is_closed
+ assert scan.json() == {"foo": "bar"}
+ assert cast(Any, scan.is_closed) is True
+ assert isinstance(scan, BinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_method_screenshot_with_all_params(self, client: Cloudflare, respx_mock: MockRouter) -> None:
+ respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock(
+ return_value=httpx.Response(200, json={"foo": "bar"})
+ )
+ scan = client.url_scanner.scans.screenshot(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ resolution="desktop",
+ )
+ assert scan.is_closed
+ assert scan.json() == {"foo": "bar"}
+ assert cast(Any, scan.is_closed) is True
+ assert isinstance(scan, BinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_raw_response_screenshot(self, client: Cloudflare, respx_mock: MockRouter) -> None:
+ respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock(
+ return_value=httpx.Response(200, json={"foo": "bar"})
+ )
+
+ scan = client.url_scanner.scans.with_raw_response.screenshot(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+
+ assert scan.is_closed is True
+ assert scan.http_request.headers.get("X-Stainless-Lang") == "python"
+ assert scan.json() == {"foo": "bar"}
+ assert isinstance(scan, BinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_streaming_response_screenshot(self, client: Cloudflare, respx_mock: MockRouter) -> None:
+ respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock(
+ return_value=httpx.Response(200, json={"foo": "bar"})
+ )
+ with client.url_scanner.scans.with_streaming_response.screenshot(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ ) as scan:
+ assert not scan.is_closed
+ assert scan.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ assert scan.json() == {"foo": "bar"}
+ assert cast(Any, scan.is_closed) is True
+ assert isinstance(scan, StreamedBinaryAPIResponse)
+
+ assert cast(Any, scan.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ def test_path_params_screenshot(self, client: Cloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ client.url_scanner.scans.with_raw_response.screenshot(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"):
+ client.url_scanner.scans.with_raw_response.screenshot(
+ scan_id="",
+ account_id="account_id",
+ )
+
+
+class TestAsyncScans:
+ parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncCloudflare) -> None:
+ scan = await async_client.url_scanner.scans.create(
+ account_id="account_id",
+ url="https://www.example.com",
+ )
+ assert_matches_type(str, scan, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ scan = await async_client.url_scanner.scans.create(
+ account_id="account_id",
+ url="https://www.example.com",
+ customagent="customagent",
+ custom_headers={"foo": "string"},
+ referer="referer",
+ screenshots_resolutions=["desktop"],
+ visibility="Public",
+ )
+ assert_matches_type(str, scan, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.url_scanner.scans.with_raw_response.create(
+ account_id="account_id",
+ url="https://www.example.com",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ scan = await response.parse()
+ assert_matches_type(str, scan, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.url_scanner.scans.with_streaming_response.create(
+ account_id="account_id",
+ url="https://www.example.com",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ scan = await response.parse()
+ assert_matches_type(str, scan, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.url_scanner.scans.with_raw_response.create(
+ account_id="",
+ url="https://www.example.com",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncCloudflare) -> None:
+ scan = await async_client.url_scanner.scans.list(
+ account_id="account_id",
+ )
+ assert_matches_type(ScanListResponse, scan, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncCloudflare) -> None:
+ scan = await async_client.url_scanner.scans.list(
+ account_id="account_id",
+ q="q",
+ size=100,
+ )
+ assert_matches_type(ScanListResponse, scan, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.url_scanner.scans.with_raw_response.list(
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ scan = await response.parse()
+ assert_matches_type(ScanListResponse, scan, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.url_scanner.scans.with_streaming_response.list(
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ scan = await response.parse()
+ assert_matches_type(ScanListResponse, scan, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_list(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.url_scanner.scans.with_raw_response.list(
+ account_id="",
+ )
+
+ @parametrize
+ async def test_method_bulk_create(self, async_client: AsyncCloudflare) -> None:
+ scan = await async_client.url_scanner.scans.bulk_create(
+ account_id="account_id",
+ body=[{"url": "https://www.example.com"}],
+ )
+ assert_matches_type(ScanBulkCreateResponse, scan, path=["response"])
+
+ @parametrize
+ async def test_raw_response_bulk_create(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.url_scanner.scans.with_raw_response.bulk_create(
+ account_id="account_id",
+ body=[{"url": "https://www.example.com"}],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ scan = await response.parse()
+ assert_matches_type(ScanBulkCreateResponse, scan, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_bulk_create(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.url_scanner.scans.with_streaming_response.bulk_create(
+ account_id="account_id",
+ body=[{"url": "https://www.example.com"}],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ scan = await response.parse()
+ assert_matches_type(ScanBulkCreateResponse, scan, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_bulk_create(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.url_scanner.scans.with_raw_response.bulk_create(
+ account_id="",
+ body=[{"url": "https://www.example.com"}],
+ )
+
+ @parametrize
+ async def test_method_dom(self, async_client: AsyncCloudflare) -> None:
+ scan = await async_client.url_scanner.scans.dom(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+ assert_matches_type(str, scan, path=["response"])
+
+ @parametrize
+ async def test_raw_response_dom(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.url_scanner.scans.with_raw_response.dom(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ scan = await response.parse()
+ assert_matches_type(str, scan, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_dom(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.url_scanner.scans.with_streaming_response.dom(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ scan = await response.parse()
+ assert_matches_type(str, scan, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_dom(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.url_scanner.scans.with_raw_response.dom(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"):
+ await async_client.url_scanner.scans.with_raw_response.dom(
+ scan_id="",
+ account_id="account_id",
+ )
+
+ @parametrize
+ async def test_method_get(self, async_client: AsyncCloudflare) -> None:
+ scan = await async_client.url_scanner.scans.get(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+ assert_matches_type(ScanGetResponse, scan, path=["response"])
+
+ @parametrize
+ async def test_raw_response_get(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.url_scanner.scans.with_raw_response.get(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ scan = await response.parse()
+ assert_matches_type(ScanGetResponse, scan, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_get(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.url_scanner.scans.with_streaming_response.get(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ scan = await response.parse()
+ assert_matches_type(ScanGetResponse, scan, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_get(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.url_scanner.scans.with_raw_response.get(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"):
+ await async_client.url_scanner.scans.with_raw_response.get(
+ scan_id="",
+ account_id="account_id",
+ )
+
+ @parametrize
+ async def test_method_har(self, async_client: AsyncCloudflare) -> None:
+ scan = await async_client.url_scanner.scans.har(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+ assert_matches_type(ScanHARResponse, scan, path=["response"])
+
+ @parametrize
+ async def test_raw_response_har(self, async_client: AsyncCloudflare) -> None:
+ response = await async_client.url_scanner.scans.with_raw_response.har(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ scan = await response.parse()
+ assert_matches_type(ScanHARResponse, scan, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_har(self, async_client: AsyncCloudflare) -> None:
+ async with async_client.url_scanner.scans.with_streaming_response.har(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ scan = await response.parse()
+ assert_matches_type(ScanHARResponse, scan, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_har(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.url_scanner.scans.with_raw_response.har(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"):
+ await async_client.url_scanner.scans.with_raw_response.har(
+ scan_id="",
+ account_id="account_id",
+ )
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_method_screenshot(self, async_client: AsyncCloudflare, respx_mock: MockRouter) -> None:
+ respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock(
+ return_value=httpx.Response(200, json={"foo": "bar"})
+ )
+ scan = await async_client.url_scanner.scans.screenshot(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+ assert scan.is_closed
+ assert await scan.json() == {"foo": "bar"}
+ assert cast(Any, scan.is_closed) is True
+ assert isinstance(scan, AsyncBinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_method_screenshot_with_all_params(
+ self, async_client: AsyncCloudflare, respx_mock: MockRouter
+ ) -> None:
+ respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock(
+ return_value=httpx.Response(200, json={"foo": "bar"})
+ )
+ scan = await async_client.url_scanner.scans.screenshot(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ resolution="desktop",
+ )
+ assert scan.is_closed
+ assert await scan.json() == {"foo": "bar"}
+ assert cast(Any, scan.is_closed) is True
+ assert isinstance(scan, AsyncBinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_raw_response_screenshot(self, async_client: AsyncCloudflare, respx_mock: MockRouter) -> None:
+ respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock(
+ return_value=httpx.Response(200, json={"foo": "bar"})
+ )
+
+ scan = await async_client.url_scanner.scans.with_raw_response.screenshot(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ )
+
+ assert scan.is_closed is True
+ assert scan.http_request.headers.get("X-Stainless-Lang") == "python"
+ assert await scan.json() == {"foo": "bar"}
+ assert isinstance(scan, AsyncBinaryAPIResponse)
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_streaming_response_screenshot(self, async_client: AsyncCloudflare, respx_mock: MockRouter) -> None:
+ respx_mock.get("/accounts/account_id/urlscanner/v2/screenshots/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e.png").mock(
+ return_value=httpx.Response(200, json={"foo": "bar"})
+ )
+ async with async_client.url_scanner.scans.with_streaming_response.screenshot(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="account_id",
+ ) as scan:
+ assert not scan.is_closed
+ assert scan.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ assert await scan.json() == {"foo": "bar"}
+ assert cast(Any, scan.is_closed) is True
+ assert isinstance(scan, AsyncStreamedBinaryAPIResponse)
+
+ assert cast(Any, scan.is_closed) is True
+
+ @parametrize
+ @pytest.mark.respx(base_url=base_url)
+ async def test_path_params_screenshot(self, async_client: AsyncCloudflare) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `account_id` but received ''"):
+ await async_client.url_scanner.scans.with_raw_response.screenshot(
+ scan_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ account_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `scan_id` but received ''"):
+ await async_client.url_scanner.scans.with_raw_response.screenshot(
+ scan_id="",
+ account_id="account_id",
+ )