From 853f56b503807b1bb41f4ca26800b56fc41e395c Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sat, 8 Aug 2026 06:56:56 +0000
Subject: [PATCH 01/11] feat(api): api update
---
.stats.yml | 4 +-
src/context/dev/resources/web.py | 44 +++++++++----------
.../dev/types/batch_get_results_response.py | 15 +++++++
.../dev/types/web_web_crawl_md_response.py | 24 +++++++++-
.../dev/types/web_web_scrape_html_response.py | 23 +++++++++-
.../dev/types/web_web_scrape_md_response.py | 23 +++++++++-
6 files changed, 106 insertions(+), 27 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 97db86d..90b5514 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 40
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-a0dda03bbb600917cfb9add468cc4c8c84351a8dbbf61644dbc353263ca1748f.yml
-openapi_spec_hash: c24264f32a46d9317aac5af9d6a396f7
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-1c93116e47cab52ea63490f5bca186ed3acda8bbd7f0ce5a320effb9f5b72b1f.yml
+openapi_spec_hash: f56204d55bd4bffb6e4d50d6a5d9471a
config_hash: 920678668dd2da6f8966fbf1b8fde4e2
diff --git a/src/context/dev/resources/web.py b/src/context/dev/resources/web.py
index efe42f7..fee68c3 100644
--- a/src/context/dev/resources/web.py
+++ b/src/context/dev/resources/web.py
@@ -2061,17 +2061,17 @@ def web_scrape_md(
### Billing & errors
- | HTTP status | Billed? | Meaning |
- | ----------- | ----------------------------------------- | ---------------------------------------------------------------------------------------- |
- | 200 | Yes — 1 credit, or 2 credits with actions | Successful scrape, including a zero-length result when includeSelectors matched nothing |
- | 400 | No | Invalid input, skipped PDF, or the page could not be scraped |
- | 401 / 403 | No | Invalid/disabled key, insufficient permissions, or credits exhausted; inspect error_code |
- | 404 | No | Target page returned or fingerprinted as not found |
- | 408 | No | Request timed out |
- | 413 | No | Target content exceeds the maximum supported size (20 MB) |
- | 415 | No | Unsupported content type |
- | 429 | No | Per-minute rate limit exceeded; honor Retry-After |
- | 500 | No | Internal error |
+ | HTTP status | Billed? | Meaning |
+ | ----------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+ | 200 | Yes — 1 credit, or 2 credits with actions | Successful scrape, including a zero-length result when includeSelectors matched nothing |
+ | 400 | No | Invalid input, skipped PDF, or the page could not be scraped. error_code WEBSITE_BLOCKED specifically means the site answered with an anti-bot challenge, CAPTCHA wall, or login shell instead of the page (even when the site returned HTTP 200) — retrying later or from another country sometimes succeeds |
+ | 401 / 403 | No | Invalid/disabled key, insufficient permissions, or credits exhausted; inspect error_code |
+ | 404 | No | Target page returned or fingerprinted as not found |
+ | 408 | No | Request timed out |
+ | 413 | No | Target content exceeds the maximum supported size (20 MB) |
+ | 415 | No | Unsupported content type |
+ | 429 | No | Per-minute rate limit exceeded; honor Retry-After |
+ | 500 | No | Internal error |
Args:
url: Full URL to scrape into LLM usable Markdown (must include http:// or https://
@@ -4285,17 +4285,17 @@ async def web_scrape_md(
### Billing & errors
- | HTTP status | Billed? | Meaning |
- | ----------- | ----------------------------------------- | ---------------------------------------------------------------------------------------- |
- | 200 | Yes — 1 credit, or 2 credits with actions | Successful scrape, including a zero-length result when includeSelectors matched nothing |
- | 400 | No | Invalid input, skipped PDF, or the page could not be scraped |
- | 401 / 403 | No | Invalid/disabled key, insufficient permissions, or credits exhausted; inspect error_code |
- | 404 | No | Target page returned or fingerprinted as not found |
- | 408 | No | Request timed out |
- | 413 | No | Target content exceeds the maximum supported size (20 MB) |
- | 415 | No | Unsupported content type |
- | 429 | No | Per-minute rate limit exceeded; honor Retry-After |
- | 500 | No | Internal error |
+ | HTTP status | Billed? | Meaning |
+ | ----------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+ | 200 | Yes — 1 credit, or 2 credits with actions | Successful scrape, including a zero-length result when includeSelectors matched nothing |
+ | 400 | No | Invalid input, skipped PDF, or the page could not be scraped. error_code WEBSITE_BLOCKED specifically means the site answered with an anti-bot challenge, CAPTCHA wall, or login shell instead of the page (even when the site returned HTTP 200) — retrying later or from another country sometimes succeeds |
+ | 401 / 403 | No | Invalid/disabled key, insufficient permissions, or credits exhausted; inspect error_code |
+ | 404 | No | Target page returned or fingerprinted as not found |
+ | 408 | No | Request timed out |
+ | 413 | No | Target content exceeds the maximum supported size (20 MB) |
+ | 415 | No | Unsupported content type |
+ | 429 | No | Per-minute rate limit exceeded; honor Retry-After |
+ | 500 | No | Internal error |
Args:
url: Full URL to scrape into LLM usable Markdown (must include http:// or https://
diff --git a/src/context/dev/types/batch_get_results_response.py b/src/context/dev/types/batch_get_results_response.py
index dfb4d62..3c92127 100644
--- a/src/context/dev/types/batch_get_results_response.py
+++ b/src/context/dev/types/batch_get_results_response.py
@@ -14,6 +14,7 @@
"DataOk",
"DataOkMetadata",
"DataOkMetadataAlternate",
+ "DataOkMetadataHeading",
"DataError",
"KeyMetadata",
]
@@ -33,6 +34,14 @@ class DataOkMetadataAlternate(BaseModel):
"""Alternate resource MIME type, when present."""
+class DataOkMetadataHeading(BaseModel):
+ level: int
+ """Heading level, 1–6 (from h1–h6)."""
+
+ text: str
+ """Heading text with whitespace collapsed, truncated to 1000 characters."""
+
+
class DataOkMetadata(BaseModel):
"""Metadata extracted from the scraped page HTML."""
@@ -63,6 +72,12 @@ class DataOkMetadata(BaseModel):
favicon: Optional[str] = None
"""Resolved favicon URL, when present."""
+ headings: Optional[List[DataOkMetadataHeading]] = None
+ """Page headings (h1–h6) in document order, extracted from the unfiltered document.
+
+ Capped at the first 500 headings. Omitted when the page has none.
+ """
+
image: Optional[str] = None
"""Primary resolved preview image from Open Graph, Twitter, or image metadata."""
diff --git a/src/context/dev/types/web_web_crawl_md_response.py b/src/context/dev/types/web_web_crawl_md_response.py
index 6c8206d..6218a6c 100644
--- a/src/context/dev/types/web_web_crawl_md_response.py
+++ b/src/context/dev/types/web_web_crawl_md_response.py
@@ -6,7 +6,15 @@
from .._models import BaseModel
-__all__ = ["WebWebCrawlMdResponse", "Metadata", "Result", "ResultMetadata", "ResultMetadataAlternate", "KeyMetadata"]
+__all__ = [
+ "WebWebCrawlMdResponse",
+ "Metadata",
+ "Result",
+ "ResultMetadata",
+ "ResultMetadataAlternate",
+ "ResultMetadataHeading",
+ "KeyMetadata",
+]
class Metadata(BaseModel):
@@ -43,6 +51,14 @@ class ResultMetadataAlternate(BaseModel):
"""Alternate resource MIME type, when present."""
+class ResultMetadataHeading(BaseModel):
+ level: int
+ """Heading level, 1–6 (from h1–h6)."""
+
+ text: str
+ """Heading text with whitespace collapsed, truncated to 1000 characters."""
+
+
class ResultMetadata(BaseModel):
crawl_depth: int = FieldInfo(alias="crawlDepth")
"""Depth relative to the start URL. 0 = start URL, 1 = one link away."""
@@ -86,6 +102,12 @@ class ResultMetadata(BaseModel):
favicon: Optional[str] = None
"""Resolved favicon URL, when present."""
+ headings: Optional[List[ResultMetadataHeading]] = None
+ """Page headings (h1–h6) in document order, extracted from the unfiltered document.
+
+ Capped at the first 500 headings. Omitted when the page has none.
+ """
+
image: Optional[str] = None
"""Primary resolved preview image from Open Graph, Twitter, or image metadata."""
diff --git a/src/context/dev/types/web_web_scrape_html_response.py b/src/context/dev/types/web_web_scrape_html_response.py
index 6ea2dd9..2d92bc7 100644
--- a/src/context/dev/types/web_web_scrape_html_response.py
+++ b/src/context/dev/types/web_web_scrape_html_response.py
@@ -7,7 +7,14 @@
from .._models import BaseModel
-__all__ = ["WebWebScrapeHTMLResponse", "Metadata", "MetadataAlternate", "ActionsApplied", "KeyMetadata"]
+__all__ = [
+ "WebWebScrapeHTMLResponse",
+ "Metadata",
+ "MetadataAlternate",
+ "MetadataHeading",
+ "ActionsApplied",
+ "KeyMetadata",
+]
class MetadataAlternate(BaseModel):
@@ -24,6 +31,14 @@ class MetadataAlternate(BaseModel):
"""Alternate resource MIME type, when present."""
+class MetadataHeading(BaseModel):
+ level: int
+ """Heading level, 1–6 (from h1–h6)."""
+
+ text: str
+ """Heading text with whitespace collapsed, truncated to 1000 characters."""
+
+
class Metadata(BaseModel):
"""Metadata extracted from the scraped page HTML."""
@@ -54,6 +69,12 @@ class Metadata(BaseModel):
favicon: Optional[str] = None
"""Resolved favicon URL, when present."""
+ headings: Optional[List[MetadataHeading]] = None
+ """Page headings (h1–h6) in document order, extracted from the unfiltered document.
+
+ Capped at the first 500 headings. Omitted when the page has none.
+ """
+
image: Optional[str] = None
"""Primary resolved preview image from Open Graph, Twitter, or image metadata."""
diff --git a/src/context/dev/types/web_web_scrape_md_response.py b/src/context/dev/types/web_web_scrape_md_response.py
index e02866b..a4a92f3 100644
--- a/src/context/dev/types/web_web_scrape_md_response.py
+++ b/src/context/dev/types/web_web_scrape_md_response.py
@@ -7,7 +7,14 @@
from .._models import BaseModel
-__all__ = ["WebWebScrapeMdResponse", "Metadata", "MetadataAlternate", "ActionsApplied", "KeyMetadata"]
+__all__ = [
+ "WebWebScrapeMdResponse",
+ "Metadata",
+ "MetadataAlternate",
+ "MetadataHeading",
+ "ActionsApplied",
+ "KeyMetadata",
+]
class MetadataAlternate(BaseModel):
@@ -24,6 +31,14 @@ class MetadataAlternate(BaseModel):
"""Alternate resource MIME type, when present."""
+class MetadataHeading(BaseModel):
+ level: int
+ """Heading level, 1–6 (from h1–h6)."""
+
+ text: str
+ """Heading text with whitespace collapsed, truncated to 1000 characters."""
+
+
class Metadata(BaseModel):
"""Metadata extracted from the scraped page HTML."""
@@ -54,6 +69,12 @@ class Metadata(BaseModel):
favicon: Optional[str] = None
"""Resolved favicon URL, when present."""
+ headings: Optional[List[MetadataHeading]] = None
+ """Page headings (h1–h6) in document order, extracted from the unfiltered document.
+
+ Capped at the first 500 headings. Omitted when the page has none.
+ """
+
image: Optional[str] = None
"""Primary resolved preview image from Open Graph, Twitter, or image metadata."""
From 499fb3fdef35838f9c631560b2bc073b73f3a04a Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 10 Aug 2026 09:04:30 +0000
Subject: [PATCH 02/11] feat(api): api update
---
.stats.yml | 4 +--
src/context/dev/resources/utility.py | 36 ++++++++++---------
src/context/dev/resources/web.py | 12 +++++++
.../dev/types/batch_get_results_response.py | 6 +++-
src/context/dev/types/batch_submit_params.py | 12 +++++++
.../dev/types/utility_prefetch_params.py | 17 +++++----
.../dev/types/utility_prefetch_response.py | 7 ++--
.../dev/types/web_web_scrape_md_params.py | 7 ++++
.../dev/types/web_web_scrape_md_response.py | 7 ++++
tests/api_resources/test_batch.py | 2 ++
tests/api_resources/test_web.py | 2 ++
11 files changed, 81 insertions(+), 31 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 90b5514..f9b95cb 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 40
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-1c93116e47cab52ea63490f5bca186ed3acda8bbd7f0ce5a320effb9f5b72b1f.yml
-openapi_spec_hash: f56204d55bd4bffb6e4d50d6a5d9471a
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-7f75f39699c1298d16691b5a58b1312a229b78288fba6e2de4040ba1422e530f.yml
+openapi_spec_hash: b3ad781596e4c52fde918cfaae54a5d4
config_hash: 920678668dd2da6f8966fbf1b8fde4e2
diff --git a/src/context/dev/resources/utility.py b/src/context/dev/resources/utility.py
index d7d10f0..33b230c 100644
--- a/src/context/dev/resources/utility.py
+++ b/src/context/dev/resources/utility.py
@@ -47,7 +47,7 @@ def prefetch(
self,
*,
identifier: utility_prefetch_params.Identifier,
- type: Literal["brand"],
+ type: Literal["brand", "styleguide"],
tags: SequenceNotStr[str] | Omit = omit,
timeout_ms: int | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -57,17 +57,19 @@ def prefetch(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> UtilityPrefetchResponse:
- """Signal that you may fetch brand data soon to improve latency.
+ """Signal that you may fetch data soon to improve latency.
- The type field
- selects what to prefetch (currently only 'brand') and identifier carries exactly
- one lookup key: a domain, or an email whose domain is extracted and validated
- (free email providers and disposable email addresses are not allowed).
+ The type field selects
+ what to prefetch ('brand' queues a brand data fetch, 'styleguide' queues a
+ styleguide extraction) and identifier carries exactly one lookup key: a domain,
+ or an email whose domain is extracted and validated (free email providers and
+ disposable email addresses are not allowed).
Args:
- identifier: Identifier of the brand to prefetch. Provide exactly one of domain or email.
+ identifier: Identifier of the target to prefetch. Provide exactly one of domain or email.
- type: What to prefetch. Currently only 'brand' is supported.
+ type: What to prefetch: 'brand' warms the brand data cache, 'styleguide' warms the
+ styleguide cache.
tags: Optional tags for tracking usage. Up to 20 tags, each 1 to 50 characters.
@@ -125,7 +127,7 @@ async def prefetch(
self,
*,
identifier: utility_prefetch_params.Identifier,
- type: Literal["brand"],
+ type: Literal["brand", "styleguide"],
tags: SequenceNotStr[str] | Omit = omit,
timeout_ms: int | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -135,17 +137,19 @@ async def prefetch(
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> UtilityPrefetchResponse:
- """Signal that you may fetch brand data soon to improve latency.
+ """Signal that you may fetch data soon to improve latency.
- The type field
- selects what to prefetch (currently only 'brand') and identifier carries exactly
- one lookup key: a domain, or an email whose domain is extracted and validated
- (free email providers and disposable email addresses are not allowed).
+ The type field selects
+ what to prefetch ('brand' queues a brand data fetch, 'styleguide' queues a
+ styleguide extraction) and identifier carries exactly one lookup key: a domain,
+ or an email whose domain is extracted and validated (free email providers and
+ disposable email addresses are not allowed).
Args:
- identifier: Identifier of the brand to prefetch. Provide exactly one of domain or email.
+ identifier: Identifier of the target to prefetch. Provide exactly one of domain or email.
- type: What to prefetch. Currently only 'brand' is supported.
+ type: What to prefetch: 'brand' warms the brand data cache, 'styleguide' warms the
+ styleguide cache.
tags: Optional tags for tracking usage. Up to 20 tags, each 1 to 50 characters.
diff --git a/src/context/dev/resources/web.py b/src/context/dev/resources/web.py
index fee68c3..c39642a 100644
--- a/src/context/dev/resources/web.py
+++ b/src/context/dev/resources/web.py
@@ -2022,6 +2022,7 @@ def web_scrape_md(
exclude_selectors: Optional[SequenceNotStr[str]] | Omit = omit,
headers: Dict[str, str] | Omit = omit,
include_frames: Union[bool, Literal["true", "false"]] | Omit = omit,
+ include_html: Union[bool, Literal["true", "false"]] | Omit = omit,
include_images: Union[bool, Literal["true", "false"]] | Omit = omit,
include_links: Union[bool, Literal["true", "false"]] | Omit = omit,
include_selectors: Optional[SequenceNotStr[str]] | Omit = omit,
@@ -2094,6 +2095,10 @@ def web_scrape_md(
include_frames: When true, the contents of iframes are rendered to Markdown.
+ include_html: When true, the response also includes an `html` field with the page HTML the
+ Markdown was converted from — the same body the Scrape HTML endpoint returns for
+ the equivalent request.
+
include_images: Include image references in Markdown output
include_links: Preserve hyperlinks in Markdown output
@@ -2157,6 +2162,7 @@ def web_scrape_md(
"exclude_selectors": exclude_selectors,
"headers": headers,
"include_frames": include_frames,
+ "include_html": include_html,
"include_images": include_images,
"include_links": include_links,
"include_selectors": include_selectors,
@@ -4246,6 +4252,7 @@ async def web_scrape_md(
exclude_selectors: Optional[SequenceNotStr[str]] | Omit = omit,
headers: Dict[str, str] | Omit = omit,
include_frames: Union[bool, Literal["true", "false"]] | Omit = omit,
+ include_html: Union[bool, Literal["true", "false"]] | Omit = omit,
include_images: Union[bool, Literal["true", "false"]] | Omit = omit,
include_links: Union[bool, Literal["true", "false"]] | Omit = omit,
include_selectors: Optional[SequenceNotStr[str]] | Omit = omit,
@@ -4318,6 +4325,10 @@ async def web_scrape_md(
include_frames: When true, the contents of iframes are rendered to Markdown.
+ include_html: When true, the response also includes an `html` field with the page HTML the
+ Markdown was converted from — the same body the Scrape HTML endpoint returns for
+ the equivalent request.
+
include_images: Include image references in Markdown output
include_links: Preserve hyperlinks in Markdown output
@@ -4381,6 +4392,7 @@ async def web_scrape_md(
"exclude_selectors": exclude_selectors,
"headers": headers,
"include_frames": include_frames,
+ "include_html": include_html,
"include_images": include_images,
"include_links": include_links,
"include_selectors": include_selectors,
diff --git a/src/context/dev/types/batch_get_results_response.py b/src/context/dev/types/batch_get_results_response.py
index 3c92127..b8e6905 100644
--- a/src/context/dev/types/batch_get_results_response.py
+++ b/src/context/dev/types/batch_get_results_response.py
@@ -131,7 +131,11 @@ class DataOk(BaseModel):
"""URL as submitted, or as discovered by the crawl."""
html: Optional[str] = None
- """Raw page HTML. Present on html batches."""
+ """Page HTML.
+
+ Present on html batches, and on markdown batches submitted with
+ `options.includeHTML`.
+ """
item_id: Optional[str] = FieldInfo(alias="itemId", default=None)
"""Caller-supplied identifier echoed from submission."""
diff --git a/src/context/dev/types/batch_submit_params.py b/src/context/dev/types/batch_submit_params.py
index d52a6d1..69ea2f5 100644
--- a/src/context/dev/types/batch_submit_params.py
+++ b/src/context/dev/types/batch_submit_params.py
@@ -329,6 +329,12 @@ class InputScrapeDataMarkdownOptions(TypedDict, total=False):
Applied after `includeSelectors`, so an element matching both is removed.
"""
+ include_html: Annotated[bool, PropertyInfo(alias="includeHTML")]
+ """
+ Also include each page's HTML in its result record, as an `html` field alongside
+ the Markdown.
+ """
+
include_images: Annotated[bool, PropertyInfo(alias="includeImages")]
"""Include image references in the Markdown."""
@@ -1037,6 +1043,12 @@ class InputCrawlDataMarkdownOptions(TypedDict, total=False):
Applied after `includeSelectors`, so an element matching both is removed.
"""
+ include_html: Annotated[bool, PropertyInfo(alias="includeHTML")]
+ """
+ Also include each page's HTML in its result record, as an `html` field alongside
+ the Markdown.
+ """
+
include_images: Annotated[bool, PropertyInfo(alias="includeImages")]
"""Include image references in the Markdown."""
diff --git a/src/context/dev/types/utility_prefetch_params.py b/src/context/dev/types/utility_prefetch_params.py
index 3e82039..d230252 100644
--- a/src/context/dev/types/utility_prefetch_params.py
+++ b/src/context/dev/types/utility_prefetch_params.py
@@ -18,10 +18,13 @@
class UtilityPrefetchParams(TypedDict, total=False):
identifier: Required[Identifier]
- """Identifier of the brand to prefetch. Provide exactly one of domain or email."""
+ """Identifier of the target to prefetch. Provide exactly one of domain or email."""
- type: Required[Literal["brand"]]
- """What to prefetch. Currently only 'brand' is supported."""
+ type: Required[Literal["brand", "styleguide"]]
+ """
+ What to prefetch: 'brand' warms the brand data cache, 'styleguide' warms the
+ styleguide cache.
+ """
tags: SequenceNotStr[str]
"""Optional tags for tracking usage. Up to 20 tags, each 1 to 50 characters."""
@@ -35,17 +38,17 @@ class UtilityPrefetchParams(TypedDict, total=False):
class IdentifierUtilityPrefetchDomainIdentifier(TypedDict, total=False):
- """Prefetch brand data by domain."""
+ """Prefetch by domain."""
domain: Required[str]
- """Domain name to prefetch brand data for"""
+ """Domain name to prefetch data for"""
class IdentifierUtilityPrefetchEmailIdentifier(TypedDict, total=False):
- """Prefetch brand data by email. The domain will be extracted and validated."""
+ """Prefetch by email. The domain will be extracted and validated."""
email: Required[str]
- """Email address to prefetch brand data for.
+ """Email address to prefetch data for.
The domain will be extracted from the email. Free email providers (gmail.com,
yahoo.com, etc.) and disposable email addresses are not allowed.
diff --git a/src/context/dev/types/utility_prefetch_response.py b/src/context/dev/types/utility_prefetch_response.py
index 346abd0..8f9c3bb 100644
--- a/src/context/dev/types/utility_prefetch_response.py
+++ b/src/context/dev/types/utility_prefetch_response.py
@@ -38,8 +38,5 @@ class UtilityPrefetchResponse(BaseModel):
status: Optional[str] = None
"""Status of the response, e.g., 'ok'"""
- type: Optional[Literal["brand"]] = None
- """
- The type of prefetch that was queued, echoed from the request (currently always
- 'brand')
- """
+ type: Optional[Literal["brand", "styleguide"]] = None
+ """The type of prefetch that was queued, echoed from the request"""
diff --git a/src/context/dev/types/web_web_scrape_md_params.py b/src/context/dev/types/web_web_scrape_md_params.py
index 11b56f1..d0ca6ed 100644
--- a/src/context/dev/types/web_web_scrape_md_params.py
+++ b/src/context/dev/types/web_web_scrape_md_params.py
@@ -253,6 +253,13 @@ class WebWebScrapeMdParams(TypedDict, total=False):
include_frames: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="includeFrames")]
"""When true, the contents of iframes are rendered to Markdown."""
+ include_html: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="includeHTML")]
+ """
+ When true, the response also includes an `html` field with the page HTML the
+ Markdown was converted from — the same body the Scrape HTML endpoint returns for
+ the equivalent request.
+ """
+
include_images: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="includeImages")]
"""Include image references in Markdown output"""
diff --git a/src/context/dev/types/web_web_scrape_md_response.py b/src/context/dev/types/web_web_scrape_md_response.py
index a4a92f3..2ea9747 100644
--- a/src/context/dev/types/web_web_scrape_md_response.py
+++ b/src/context/dev/types/web_web_scrape_md_response.py
@@ -172,6 +172,13 @@ class WebWebScrapeMdResponse(BaseModel):
afterward.
"""
+ html: Optional[str] = None
+ """
+ Only present when includeHTML=true: the page HTML the Markdown was converted
+ from — the same body the Scrape HTML endpoint returns for the equivalent
+ request.
+ """
+
key_metadata: Optional[KeyMetadata] = None
"""Metadata about the API key used for the request.
diff --git a/tests/api_resources/test_batch.py b/tests/api_resources/test_batch.py
index 0691496..53ceb42 100644
--- a/tests/api_resources/test_batch.py
+++ b/tests/api_resources/test_batch.py
@@ -282,6 +282,7 @@ def test_method_submit_with_all_params(self, client: ContextDev) -> None:
"options": {
"country": "de",
"exclude_selectors": ["x"],
+ "include_html": True,
"include_images": True,
"include_links": True,
"include_selectors": ["x"],
@@ -614,6 +615,7 @@ async def test_method_submit_with_all_params(self, async_client: AsyncContextDev
"options": {
"country": "de",
"exclude_selectors": ["x"],
+ "include_html": True,
"include_images": True,
"include_links": True,
"include_selectors": ["x"],
diff --git a/tests/api_resources/test_web.py b/tests/api_resources/test_web.py
index b873aab..a4dc353 100644
--- a/tests/api_resources/test_web.py
+++ b/tests/api_resources/test_web.py
@@ -572,6 +572,7 @@ def test_method_web_scrape_md_with_all_params(self, client: ContextDev) -> None:
exclude_selectors=["x"],
headers={"foo": "J!"},
include_frames="true",
+ include_html="true",
include_images="true",
include_links="true",
include_selectors=["x"],
@@ -1217,6 +1218,7 @@ async def test_method_web_scrape_md_with_all_params(self, async_client: AsyncCon
exclude_selectors=["x"],
headers={"foo": "J!"},
include_frames="true",
+ include_html="true",
include_images="true",
include_links="true",
include_selectors=["x"],
From 31fc5ecfb9c739114997d346adfa64cefbcf7f97 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Tue, 11 Aug 2026 07:47:01 +0000
Subject: [PATCH 03/11] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index f9b95cb..0829be8 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 40
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-7f75f39699c1298d16691b5a58b1312a229b78288fba6e2de4040ba1422e530f.yml
-openapi_spec_hash: b3ad781596e4c52fde918cfaae54a5d4
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-5a88256ce88b7bc7854211e67446bc71bc52975a2d62263e1194bec7cf6c2f16.yml
+openapi_spec_hash: f5011cea07c6c4a7d2984d6be85ffe49
config_hash: 920678668dd2da6f8966fbf1b8fde4e2
From bc0321c451c3460cb81cd6e4c14fee589aaac046 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 12 Aug 2026 09:21:16 +0000
Subject: [PATCH 04/11] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 0829be8..9ed440e 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 40
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-5a88256ce88b7bc7854211e67446bc71bc52975a2d62263e1194bec7cf6c2f16.yml
-openapi_spec_hash: f5011cea07c6c4a7d2984d6be85ffe49
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-c0a0223a614d6793c76c77cb5443276adf09ffa1a4d6761aae240d30a65d71e6.yml
+openapi_spec_hash: 1aa54f261fd2238035711a73618c51cd
config_hash: 920678668dd2da6f8966fbf1b8fde4e2
From 1aacc569fd767a6119c839bbad3e2f3f448c7bfc Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Wed, 12 Aug 2026 20:43:49 +0000
Subject: [PATCH 05/11] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 9ed440e..3f88b9d 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 40
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-c0a0223a614d6793c76c77cb5443276adf09ffa1a4d6761aae240d30a65d71e6.yml
-openapi_spec_hash: 1aa54f261fd2238035711a73618c51cd
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-662e436bab5a74933ca1d08ac370c34ef632285b7aec23d95b1b442b2291d589.yml
+openapi_spec_hash: 34399fa7028ac361abee1a0a4f139093
config_hash: 920678668dd2da6f8966fbf1b8fde4e2
From 07e80168ee24305f42bfac338ee85d676e9601d7 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Thu, 13 Aug 2026 20:43:59 +0000
Subject: [PATCH 06/11] feat(api): api update
---
.stats.yml | 4 +-
src/context/dev/resources/parse.py | 21 +++--
src/context/dev/resources/web.py | 50 ++++++------
src/context/dev/types/batch_submit_params.py | 16 ++--
src/context/dev/types/parse_handle_params.py | 11 ++-
.../dev/types/web_screenshot_params.py | 4 +-
.../dev/types/web_web_scrape_html_params.py | 10 +--
.../dev/types/web_web_scrape_images_params.py | 8 +-
.../dev/types/web_web_scrape_md_params.py | 18 ++---
tests/api_resources/test_batch.py | 8 +-
tests/api_resources/test_parse.py | 20 ++---
tests/api_resources/test_web.py | 76 +++++++++----------
12 files changed, 122 insertions(+), 124 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 3f88b9d..ef63af1 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 40
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-662e436bab5a74933ca1d08ac370c34ef632285b7aec23d95b1b442b2291d589.yml
-openapi_spec_hash: 34399fa7028ac361abee1a0a4f139093
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-d31d405927ec58ac97c0a9732130aea18209c65a0f2f0a348aa91c333535305e.yml
+openapi_spec_hash: 0ede81f7fd98f41bc6bbc4167300bfb9
config_hash: 920678668dd2da6f8966fbf1b8fde4e2
diff --git a/src/context/dev/resources/parse.py b/src/context/dev/resources/parse.py
index 3c3101a..4a2a75f 100644
--- a/src/context/dev/resources/parse.py
+++ b/src/context/dev/resources/parse.py
@@ -3,7 +3,6 @@
from __future__ import annotations
import os
-from typing import Union
from typing_extensions import Literal
import httpx
@@ -136,13 +135,13 @@ def handle(
"pnm",
]
| Omit = omit,
- include_images: Union[bool, Literal["true", "false"]] | Omit = omit,
- include_links: Union[bool, Literal["true", "false"]] | Omit = omit,
- ocr: Union[bool, Literal["true", "false"]] | Omit = omit,
+ include_images: bool | Omit = omit,
+ include_links: bool | Omit = omit,
+ ocr: bool | Omit = omit,
pdf: parse_handle_params.Pdf | Omit = omit,
- shorten_base64_images: Union[bool, Literal["true", "false"]] | Omit = omit,
+ shorten_base64_images: bool | Omit = omit,
tags: SequenceNotStr[str] | Omit = omit,
- use_main_content_only: Union[bool, Literal["true", "false"]] | Omit = omit,
+ use_main_content_only: bool | Omit = omit,
zdr: Literal["enabled", "disabled"] | Omit = omit,
# 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.
@@ -321,13 +320,13 @@ async def handle(
"pnm",
]
| Omit = omit,
- include_images: Union[bool, Literal["true", "false"]] | Omit = omit,
- include_links: Union[bool, Literal["true", "false"]] | Omit = omit,
- ocr: Union[bool, Literal["true", "false"]] | Omit = omit,
+ include_images: bool | Omit = omit,
+ include_links: bool | Omit = omit,
+ ocr: bool | Omit = omit,
pdf: parse_handle_params.Pdf | Omit = omit,
- shorten_base64_images: Union[bool, Literal["true", "false"]] | Omit = omit,
+ shorten_base64_images: bool | Omit = omit,
tags: SequenceNotStr[str] | Omit = omit,
- use_main_content_only: Union[bool, Literal["true", "false"]] | Omit = omit,
+ use_main_content_only: bool | Omit = omit,
zdr: Literal["enabled", "disabled"] | Omit = omit,
# 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.
diff --git a/src/context/dev/resources/web.py b/src/context/dev/resources/web.py
index c39642a..a058849 100644
--- a/src/context/dev/resources/web.py
+++ b/src/context/dev/resources/web.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Dict, Union, Iterable, Optional
+from typing import Dict, Iterable, Optional
from typing_extensions import Literal
import httpx
@@ -597,7 +597,7 @@ def screenshot(
direct_url: str | Omit = omit,
domain: str | Omit = omit,
full_screenshot: Literal["true", "false"] | Omit = omit,
- handle_cookie_popup: Union[bool, Literal["true", "false"]] | Omit = omit,
+ handle_cookie_popup: bool | Omit = omit,
max_age_ms: Optional[int] | Omit = omit,
page: Literal["login", "signup", "blog", "careers", "pricing", "terms", "privacy", "contact"] | Omit = omit,
scroll_offset: Optional[int] | Omit = omit,
@@ -1599,14 +1599,14 @@ def web_scrape_html(
| Omit = omit,
exclude_selectors: Optional[SequenceNotStr[str]] | Omit = omit,
headers: Dict[str, str] | Omit = omit,
- include_frames: Union[bool, Literal["true", "false"]] | Omit = omit,
+ include_frames: bool | Omit = omit,
include_selectors: Optional[SequenceNotStr[str]] | Omit = omit,
max_age_ms: Optional[int] | Omit = omit,
pdf: web_web_scrape_html_params.Pdf | Omit = omit,
- settle_animations: Union[bool, Literal["true", "false"]] | Omit = omit,
+ settle_animations: bool | Omit = omit,
tags: SequenceNotStr[str] | Omit = omit,
timeout_ms: int | Omit = omit,
- use_main_content_only: Union[bool, Literal["true", "false"]] | Omit = omit,
+ use_main_content_only: bool | Omit = omit,
wait_for_ms: Optional[int] | Omit = omit,
zdr: Literal["enabled", "disabled"] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -1720,7 +1720,7 @@ def web_scrape_images(
*,
url: str,
actions: Optional[Iterable[web_web_scrape_images_params.Action]] | Omit = omit,
- dedupe: Union[bool, Literal["true", "false"]] | Omit = omit,
+ dedupe: bool | Omit = omit,
enrichment: Optional[web_web_scrape_images_params.Enrichment] | Omit = omit,
headers: Dict[str, str] | Omit = omit,
max_age_ms: Optional[int] | Omit = omit,
@@ -2021,18 +2021,18 @@ def web_scrape_md(
| Omit = omit,
exclude_selectors: Optional[SequenceNotStr[str]] | Omit = omit,
headers: Dict[str, str] | Omit = omit,
- include_frames: Union[bool, Literal["true", "false"]] | Omit = omit,
- include_html: Union[bool, Literal["true", "false"]] | Omit = omit,
- include_images: Union[bool, Literal["true", "false"]] | Omit = omit,
- include_links: Union[bool, Literal["true", "false"]] | Omit = omit,
+ include_frames: bool | Omit = omit,
+ include_html: bool | Omit = omit,
+ include_images: bool | Omit = omit,
+ include_links: bool | Omit = omit,
include_selectors: Optional[SequenceNotStr[str]] | Omit = omit,
max_age_ms: Optional[int] | Omit = omit,
pdf: web_web_scrape_md_params.Pdf | Omit = omit,
- settle_animations: Union[bool, Literal["true", "false"]] | Omit = omit,
- shorten_base64_images: Union[bool, Literal["true", "false"]] | Omit = omit,
+ settle_animations: bool | Omit = omit,
+ shorten_base64_images: bool | Omit = omit,
tags: SequenceNotStr[str] | Omit = omit,
timeout_ms: int | Omit = omit,
- use_main_content_only: Union[bool, Literal["true", "false"]] | Omit = omit,
+ use_main_content_only: bool | Omit = omit,
wait_for_ms: Optional[int] | Omit = omit,
zdr: Literal["enabled", "disabled"] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -2827,7 +2827,7 @@ async def screenshot(
direct_url: str | Omit = omit,
domain: str | Omit = omit,
full_screenshot: Literal["true", "false"] | Omit = omit,
- handle_cookie_popup: Union[bool, Literal["true", "false"]] | Omit = omit,
+ handle_cookie_popup: bool | Omit = omit,
max_age_ms: Optional[int] | Omit = omit,
page: Literal["login", "signup", "blog", "careers", "pricing", "terms", "privacy", "contact"] | Omit = omit,
scroll_offset: Optional[int] | Omit = omit,
@@ -3829,14 +3829,14 @@ async def web_scrape_html(
| Omit = omit,
exclude_selectors: Optional[SequenceNotStr[str]] | Omit = omit,
headers: Dict[str, str] | Omit = omit,
- include_frames: Union[bool, Literal["true", "false"]] | Omit = omit,
+ include_frames: bool | Omit = omit,
include_selectors: Optional[SequenceNotStr[str]] | Omit = omit,
max_age_ms: Optional[int] | Omit = omit,
pdf: web_web_scrape_html_params.Pdf | Omit = omit,
- settle_animations: Union[bool, Literal["true", "false"]] | Omit = omit,
+ settle_animations: bool | Omit = omit,
tags: SequenceNotStr[str] | Omit = omit,
timeout_ms: int | Omit = omit,
- use_main_content_only: Union[bool, Literal["true", "false"]] | Omit = omit,
+ use_main_content_only: bool | Omit = omit,
wait_for_ms: Optional[int] | Omit = omit,
zdr: Literal["enabled", "disabled"] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -3950,7 +3950,7 @@ async def web_scrape_images(
*,
url: str,
actions: Optional[Iterable[web_web_scrape_images_params.Action]] | Omit = omit,
- dedupe: Union[bool, Literal["true", "false"]] | Omit = omit,
+ dedupe: bool | Omit = omit,
enrichment: Optional[web_web_scrape_images_params.Enrichment] | Omit = omit,
headers: Dict[str, str] | Omit = omit,
max_age_ms: Optional[int] | Omit = omit,
@@ -4251,18 +4251,18 @@ async def web_scrape_md(
| Omit = omit,
exclude_selectors: Optional[SequenceNotStr[str]] | Omit = omit,
headers: Dict[str, str] | Omit = omit,
- include_frames: Union[bool, Literal["true", "false"]] | Omit = omit,
- include_html: Union[bool, Literal["true", "false"]] | Omit = omit,
- include_images: Union[bool, Literal["true", "false"]] | Omit = omit,
- include_links: Union[bool, Literal["true", "false"]] | Omit = omit,
+ include_frames: bool | Omit = omit,
+ include_html: bool | Omit = omit,
+ include_images: bool | Omit = omit,
+ include_links: bool | Omit = omit,
include_selectors: Optional[SequenceNotStr[str]] | Omit = omit,
max_age_ms: Optional[int] | Omit = omit,
pdf: web_web_scrape_md_params.Pdf | Omit = omit,
- settle_animations: Union[bool, Literal["true", "false"]] | Omit = omit,
- shorten_base64_images: Union[bool, Literal["true", "false"]] | Omit = omit,
+ settle_animations: bool | Omit = omit,
+ shorten_base64_images: bool | Omit = omit,
tags: SequenceNotStr[str] | Omit = omit,
timeout_ms: int | Omit = omit,
- use_main_content_only: Union[bool, Literal["true", "false"]] | Omit = omit,
+ use_main_content_only: bool | Omit = omit,
wait_for_ms: Optional[int] | Omit = omit,
zdr: Literal["enabled", "disabled"] | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
diff --git a/src/context/dev/types/batch_submit_params.py b/src/context/dev/types/batch_submit_params.py
index 69ea2f5..ec7335b 100644
--- a/src/context/dev/types/batch_submit_params.py
+++ b/src/context/dev/types/batch_submit_params.py
@@ -88,7 +88,7 @@ class InputScrapeDataMarkdownOptionsPdf(TypedDict, total=False):
start when both are provided.
"""
- ocr: Union[bool, Literal["true", "false"]]
+ ocr: bool
"""
When true, OCR the selected PDF pages that have no usable text layer (scans),
replacing each recovered page's text with the OCR result while pages with a real
@@ -96,7 +96,7 @@ class InputScrapeDataMarkdownOptionsPdf(TypedDict, total=False):
of the base request cost. When false, no OCR runs.
"""
- should_parse: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="shouldParse")]
+ should_parse: Annotated[bool, PropertyInfo(alias="shouldParse")]
"""When true, PDF URLs are fetched and parsed.
When false, PDF URLs are skipped and a 400 PDF_SKIPPED is returned.
@@ -419,7 +419,7 @@ class InputScrapeDataHTMLOptionsPdf(TypedDict, total=False):
start when both are provided.
"""
- ocr: Union[bool, Literal["true", "false"]]
+ ocr: bool
"""
When true, OCR the selected PDF pages that have no usable text layer (scans),
replacing each recovered page's text with the OCR result while pages with a real
@@ -427,7 +427,7 @@ class InputScrapeDataHTMLOptionsPdf(TypedDict, total=False):
of the base request cost. When false, no OCR runs.
"""
- should_parse: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="shouldParse")]
+ should_parse: Annotated[bool, PropertyInfo(alias="shouldParse")]
"""When true, PDF URLs are fetched and parsed.
When false, PDF URLs are skipped and a 400 PDF_SKIPPED is returned.
@@ -802,7 +802,7 @@ class InputCrawlDataMarkdownOptionsPdf(TypedDict, total=False):
start when both are provided.
"""
- ocr: Union[bool, Literal["true", "false"]]
+ ocr: bool
"""
When true, OCR the selected PDF pages that have no usable text layer (scans),
replacing each recovered page's text with the OCR result while pages with a real
@@ -810,7 +810,7 @@ class InputCrawlDataMarkdownOptionsPdf(TypedDict, total=False):
of the base request cost. When false, no OCR runs.
"""
- should_parse: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="shouldParse")]
+ should_parse: Annotated[bool, PropertyInfo(alias="shouldParse")]
"""When true, PDF URLs are fetched and parsed.
When false, PDF URLs are skipped and a 400 PDF_SKIPPED is returned.
@@ -1185,7 +1185,7 @@ class InputCrawlDataHTMLOptionsPdf(TypedDict, total=False):
start when both are provided.
"""
- ocr: Union[bool, Literal["true", "false"]]
+ ocr: bool
"""
When true, OCR the selected PDF pages that have no usable text layer (scans),
replacing each recovered page's text with the OCR result while pages with a real
@@ -1193,7 +1193,7 @@ class InputCrawlDataHTMLOptionsPdf(TypedDict, total=False):
of the base request cost. When false, no OCR runs.
"""
- should_parse: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="shouldParse")]
+ should_parse: Annotated[bool, PropertyInfo(alias="shouldParse")]
"""When true, PDF URLs are fetched and parsed.
When false, PDF URLs are skipped and a 400 PDF_SKIPPED is returned.
diff --git a/src/context/dev/types/parse_handle_params.py b/src/context/dev/types/parse_handle_params.py
index 26676e1..7b34545 100644
--- a/src/context/dev/types/parse_handle_params.py
+++ b/src/context/dev/types/parse_handle_params.py
@@ -2,7 +2,6 @@
from __future__ import annotations
-from typing import Union
from typing_extensions import Literal, Annotated, TypedDict
from .._types import SequenceNotStr
@@ -92,13 +91,13 @@ class ParseHandleParams(TypedDict, total=False):
md, py, rtf, jpg, png, or txt.
"""
- include_images: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="includeImages")]
+ include_images: Annotated[bool, PropertyInfo(alias="includeImages")]
"""Include image references in Markdown output"""
- include_links: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="includeLinks")]
+ include_links: Annotated[bool, PropertyInfo(alias="includeLinks")]
"""Preserve hyperlinks in Markdown output"""
- ocr: Union[bool, Literal["true", "false"]]
+ ocr: bool
"""
When true for PDF inputs, OCR the selected pages that have no usable text layer
(scans), replacing each recovered page's text with the OCR result while pages
@@ -110,7 +109,7 @@ class ParseHandleParams(TypedDict, total=False):
pdf: Pdf
"""PDF page-range options as a JSON object, e.g. {"start": 2, "end": 5}."""
- shorten_base64_images: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="shortenBase64Images")]
+ shorten_base64_images: Annotated[bool, PropertyInfo(alias="shortenBase64Images")]
"""Shorten base64-encoded image data in the Markdown output"""
tags: SequenceNotStr[str]
@@ -120,7 +119,7 @@ class ParseHandleParams(TypedDict, total=False):
the dashboard usage page. Up to 20 tags, each 1-50 characters.
"""
- use_main_content_only: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="useMainContentOnly")]
+ use_main_content_only: Annotated[bool, PropertyInfo(alias="useMainContentOnly")]
"""Extract only the main content from HTML-like inputs"""
zdr: Literal["enabled", "disabled"]
diff --git a/src/context/dev/types/web_screenshot_params.py b/src/context/dev/types/web_screenshot_params.py
index 830e1a7..2442d70 100644
--- a/src/context/dev/types/web_screenshot_params.py
+++ b/src/context/dev/types/web_screenshot_params.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Union, Optional
+from typing import Optional
from typing_extensions import Literal, Annotated, TypedDict
from .._types import SequenceNotStr
@@ -250,7 +250,7 @@ class WebScreenshotParams(TypedDict, total=False):
provided, takes a viewport screenshot (standard browser view).
"""
- handle_cookie_popup: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="handleCookiePopup")]
+ handle_cookie_popup: Annotated[bool, PropertyInfo(alias="handleCookiePopup")]
"""Optional parameter to control cookie/consent popup handling.
If 'true', we dismiss cookie banner before capture. If 'false' or not provided,
diff --git a/src/context/dev/types/web_web_scrape_html_params.py b/src/context/dev/types/web_web_scrape_html_params.py
index 461974a..df85c86 100644
--- a/src/context/dev/types/web_web_scrape_html_params.py
+++ b/src/context/dev/types/web_web_scrape_html_params.py
@@ -247,7 +247,7 @@ class WebWebScrapeHTMLParams(TypedDict, total=False):
is bypassed: the result is neither read from nor written to cache.
"""
- include_frames: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="includeFrames")]
+ include_frames: Annotated[bool, PropertyInfo(alias="includeFrames")]
"""When true, iframes are rendered inline into the returned HTML."""
include_selectors: Annotated[Optional[SequenceNotStr[str]], PropertyInfo(alias="includeSelectors")]
@@ -272,7 +272,7 @@ class WebWebScrapeHTMLParams(TypedDict, total=False):
inclusive 1-based page range.
"""
- settle_animations: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="settleAnimations")]
+ settle_animations: Annotated[bool, PropertyInfo(alias="settleAnimations")]
"""
When true, waits briefly for CSS and transition animations to settle before
extracting HTML. Defaults to false. This adds a bit of latency in exchange for
@@ -293,7 +293,7 @@ class WebWebScrapeHTMLParams(TypedDict, total=False):
status code. Maximum allowed value is 300000ms (5 minutes).
"""
- use_main_content_only: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="useMainContentOnly")]
+ use_main_content_only: Annotated[bool, PropertyInfo(alias="useMainContentOnly")]
"""
When true, return only the page's main content in the HTML response, excluding
headers, footers, sidebars, and navigation when detectable.
@@ -346,7 +346,7 @@ class Pdf(TypedDict, total=False):
start when both are provided.
"""
- ocr: Union[bool, Literal["true", "false"]]
+ ocr: bool
"""
When true, OCR the selected PDF pages that have no usable text layer (scans),
replacing each recovered page's text with the OCR result while pages with a real
@@ -354,7 +354,7 @@ class Pdf(TypedDict, total=False):
of the base request cost. When false, no OCR runs.
"""
- should_parse: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="shouldParse")]
+ should_parse: Annotated[bool, PropertyInfo(alias="shouldParse")]
"""When true, PDF URLs are fetched and parsed.
When false, PDF URLs are skipped and a 400 PDF_SKIPPED is returned.
diff --git a/src/context/dev/types/web_web_scrape_images_params.py b/src/context/dev/types/web_web_scrape_images_params.py
index b2fb384..952dc76 100644
--- a/src/context/dev/types/web_web_scrape_images_params.py
+++ b/src/context/dev/types/web_web_scrape_images_params.py
@@ -28,7 +28,7 @@ class WebWebScrapeImagesParams(TypedDict, total=False):
parameter. Maximum: 5 actions.
"""
- dedupe: Union[bool, Literal["true", "false"]]
+ dedupe: bool
"""
When true, visually duplicate images are removed: every image is loaded and
perceptually hashed, and only the highest-resolution copy of each duplicate
@@ -101,10 +101,10 @@ class Enrichment(TypedDict, total=False):
Optional per-image processing, sent as deep-object query params such as enrichment[resolution]=true.
"""
- classification: Union[bool, Literal["true", "false"]]
+ classification: bool
"""Classify each image by visual asset type."""
- hosted_url: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="hostedUrl")]
+ hosted_url: Annotated[bool, PropertyInfo(alias="hostedUrl")]
"""
Host materializable images on the Brand.dev CDN and return their URL and MIME
type.
@@ -113,5 +113,5 @@ class Enrichment(TypedDict, total=False):
max_time_per_ms: Annotated[int, PropertyInfo(alias="maxTimePerMs")]
"""Per-image enrichment timeout in milliseconds. Default: 30000. Maximum: 60000."""
- resolution: Union[bool, Literal["true", "false"]]
+ resolution: bool
"""Measure image width and height when possible."""
diff --git a/src/context/dev/types/web_web_scrape_md_params.py b/src/context/dev/types/web_web_scrape_md_params.py
index d0ca6ed..e655fd6 100644
--- a/src/context/dev/types/web_web_scrape_md_params.py
+++ b/src/context/dev/types/web_web_scrape_md_params.py
@@ -250,20 +250,20 @@ class WebWebScrapeMdParams(TypedDict, total=False):
is bypassed: the result is neither read from nor written to cache.
"""
- include_frames: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="includeFrames")]
+ include_frames: Annotated[bool, PropertyInfo(alias="includeFrames")]
"""When true, the contents of iframes are rendered to Markdown."""
- include_html: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="includeHTML")]
+ include_html: Annotated[bool, PropertyInfo(alias="includeHTML")]
"""
When true, the response also includes an `html` field with the page HTML the
Markdown was converted from — the same body the Scrape HTML endpoint returns for
the equivalent request.
"""
- include_images: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="includeImages")]
+ include_images: Annotated[bool, PropertyInfo(alias="includeImages")]
"""Include image references in Markdown output"""
- include_links: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="includeLinks")]
+ include_links: Annotated[bool, PropertyInfo(alias="includeLinks")]
"""Preserve hyperlinks in Markdown output"""
include_selectors: Annotated[Optional[SequenceNotStr[str]], PropertyInfo(alias="includeSelectors")]
@@ -288,14 +288,14 @@ class WebWebScrapeMdParams(TypedDict, total=False):
inclusive 1-based page range.
"""
- settle_animations: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="settleAnimations")]
+ settle_animations: Annotated[bool, PropertyInfo(alias="settleAnimations")]
"""
When true, waits briefly for CSS and transition animations to settle before
converting to Markdown. Defaults to false. This adds a bit of latency in
exchange for more stable output on animated pages.
"""
- shorten_base64_images: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="shortenBase64Images")]
+ shorten_base64_images: Annotated[bool, PropertyInfo(alias="shortenBase64Images")]
"""Shorten base64-encoded image data in the Markdown output"""
tags: SequenceNotStr[str]
@@ -312,7 +312,7 @@ class WebWebScrapeMdParams(TypedDict, total=False):
status code. Maximum allowed value is 300000ms (5 minutes).
"""
- use_main_content_only: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="useMainContentOnly")]
+ use_main_content_only: Annotated[bool, PropertyInfo(alias="useMainContentOnly")]
"""
Extract only the main content of the page, excluding headers, footers, sidebars,
and navigation
@@ -365,7 +365,7 @@ class Pdf(TypedDict, total=False):
start when both are provided.
"""
- ocr: Union[bool, Literal["true", "false"]]
+ ocr: bool
"""
When true, OCR the selected PDF pages that have no usable text layer (scans),
replacing each recovered page's text with the OCR result while pages with a real
@@ -373,7 +373,7 @@ class Pdf(TypedDict, total=False):
of the base request cost. When false, no OCR runs.
"""
- should_parse: Annotated[Union[bool, Literal["true", "false"]], PropertyInfo(alias="shouldParse")]
+ should_parse: Annotated[bool, PropertyInfo(alias="shouldParse")]
"""When true, PDF URLs are fetched and parsed.
When false, PDF URLs are skipped and a 400 PDF_SKIPPED is returned.
diff --git a/tests/api_resources/test_batch.py b/tests/api_resources/test_batch.py
index 53ceb42..9e91446 100644
--- a/tests/api_resources/test_batch.py
+++ b/tests/api_resources/test_batch.py
@@ -289,8 +289,8 @@ def test_method_submit_with_all_params(self, client: ContextDev) -> None:
"max_age_ms": 0,
"pdf": {
"end": 1,
- "ocr": "true",
- "should_parse": "true",
+ "ocr": True,
+ "should_parse": True,
"start": 1,
},
"settle_animations": True,
@@ -622,8 +622,8 @@ async def test_method_submit_with_all_params(self, async_client: AsyncContextDev
"max_age_ms": 0,
"pdf": {
"end": 1,
- "ocr": "true",
- "should_parse": "true",
+ "ocr": True,
+ "should_parse": True,
"start": 1,
},
"settle_animations": True,
diff --git a/tests/api_resources/test_parse.py b/tests/api_resources/test_parse.py
index d42c128..006aede 100644
--- a/tests/api_resources/test_parse.py
+++ b/tests/api_resources/test_parse.py
@@ -32,16 +32,16 @@ def test_method_handle_with_all_params(self, client: ContextDev) -> None:
body=b"Example data",
client="x",
extension="txt",
- include_images="true",
- include_links="true",
- ocr="true",
+ include_images=True,
+ include_links=True,
+ ocr=True,
pdf={
"end": 1,
"start": 1,
},
- shorten_base64_images="true",
+ shorten_base64_images=True,
tags=["production", "team-alpha"],
- use_main_content_only="true",
+ use_main_content_only=True,
zdr="enabled",
)
assert_matches_type(ParseHandleResponse, parse, path=["response"])
@@ -93,16 +93,16 @@ async def test_method_handle_with_all_params(self, async_client: AsyncContextDev
body=b"Example data",
client="x",
extension="txt",
- include_images="true",
- include_links="true",
- ocr="true",
+ include_images=True,
+ include_links=True,
+ ocr=True,
pdf={
"end": 1,
"start": 1,
},
- shorten_base64_images="true",
+ shorten_base64_images=True,
tags=["production", "team-alpha"],
- use_main_content_only="true",
+ use_main_content_only=True,
zdr="enabled",
)
assert_matches_type(ParseHandleResponse, parse, path=["response"])
diff --git a/tests/api_resources/test_web.py b/tests/api_resources/test_web.py
index a4dc353..724e013 100644
--- a/tests/api_resources/test_web.py
+++ b/tests/api_resources/test_web.py
@@ -253,7 +253,7 @@ def test_method_screenshot_with_all_params(self, client: ContextDev) -> None:
direct_url="https://example.com",
domain="xxx",
full_screenshot="true",
- handle_cookie_popup="true",
+ handle_cookie_popup=True,
max_age_ms=0,
page="login",
scroll_offset=0,
@@ -445,19 +445,19 @@ def test_method_web_scrape_html_with_all_params(self, client: ContextDev) -> Non
country="de",
exclude_selectors=["x"],
headers={"foo": "J!"},
- include_frames="true",
+ include_frames=True,
include_selectors=["x"],
max_age_ms=0,
pdf={
"end": 1,
- "ocr": "true",
- "should_parse": "true",
+ "ocr": True,
+ "should_parse": True,
"start": 1,
},
- settle_animations="true",
+ settle_animations=True,
tags=["production", "team-alpha"],
timeout_ms=1,
- use_main_content_only="true",
+ use_main_content_only=True,
wait_for_ms=0,
zdr="enabled",
)
@@ -508,12 +508,12 @@ def test_method_web_scrape_images_with_all_params(self, client: ContextDev) -> N
"time_ms": 0,
}
],
- dedupe="true",
+ dedupe=True,
enrichment={
- "classification": "true",
- "hosted_url": "true",
+ "classification": True,
+ "hosted_url": True,
"max_time_per_ms": 1,
- "resolution": "true",
+ "resolution": True,
},
headers={"foo": "J!"},
max_age_ms=0,
@@ -571,23 +571,23 @@ def test_method_web_scrape_md_with_all_params(self, client: ContextDev) -> None:
country="de",
exclude_selectors=["x"],
headers={"foo": "J!"},
- include_frames="true",
- include_html="true",
- include_images="true",
- include_links="true",
+ include_frames=True,
+ include_html=True,
+ include_images=True,
+ include_links=True,
include_selectors=["x"],
max_age_ms=0,
pdf={
"end": 1,
- "ocr": "true",
- "should_parse": "true",
+ "ocr": True,
+ "should_parse": True,
"start": 1,
},
- settle_animations="true",
- shorten_base64_images="true",
+ settle_animations=True,
+ shorten_base64_images=True,
tags=["production", "team-alpha"],
timeout_ms=1,
- use_main_content_only="true",
+ use_main_content_only=True,
wait_for_ms=0,
zdr="enabled",
)
@@ -899,7 +899,7 @@ async def test_method_screenshot_with_all_params(self, async_client: AsyncContex
direct_url="https://example.com",
domain="xxx",
full_screenshot="true",
- handle_cookie_popup="true",
+ handle_cookie_popup=True,
max_age_ms=0,
page="login",
scroll_offset=0,
@@ -1091,19 +1091,19 @@ async def test_method_web_scrape_html_with_all_params(self, async_client: AsyncC
country="de",
exclude_selectors=["x"],
headers={"foo": "J!"},
- include_frames="true",
+ include_frames=True,
include_selectors=["x"],
max_age_ms=0,
pdf={
"end": 1,
- "ocr": "true",
- "should_parse": "true",
+ "ocr": True,
+ "should_parse": True,
"start": 1,
},
- settle_animations="true",
+ settle_animations=True,
tags=["production", "team-alpha"],
timeout_ms=1,
- use_main_content_only="true",
+ use_main_content_only=True,
wait_for_ms=0,
zdr="enabled",
)
@@ -1154,12 +1154,12 @@ async def test_method_web_scrape_images_with_all_params(self, async_client: Asyn
"time_ms": 0,
}
],
- dedupe="true",
+ dedupe=True,
enrichment={
- "classification": "true",
- "hosted_url": "true",
+ "classification": True,
+ "hosted_url": True,
"max_time_per_ms": 1,
- "resolution": "true",
+ "resolution": True,
},
headers={"foo": "J!"},
max_age_ms=0,
@@ -1217,23 +1217,23 @@ async def test_method_web_scrape_md_with_all_params(self, async_client: AsyncCon
country="de",
exclude_selectors=["x"],
headers={"foo": "J!"},
- include_frames="true",
- include_html="true",
- include_images="true",
- include_links="true",
+ include_frames=True,
+ include_html=True,
+ include_images=True,
+ include_links=True,
include_selectors=["x"],
max_age_ms=0,
pdf={
"end": 1,
- "ocr": "true",
- "should_parse": "true",
+ "ocr": True,
+ "should_parse": True,
"start": 1,
},
- settle_animations="true",
- shorten_base64_images="true",
+ settle_animations=True,
+ shorten_base64_images=True,
tags=["production", "team-alpha"],
timeout_ms=1,
- use_main_content_only="true",
+ use_main_content_only=True,
wait_for_ms=0,
zdr="enabled",
)
From 721b3966e20f7974654ac6a49a6a56e8efba2e2b Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Fri, 14 Aug 2026 01:59:34 +0000
Subject: [PATCH 07/11] codegen metadata
---
.stats.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index ef63af1..f194dd9 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 40
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-d31d405927ec58ac97c0a9732130aea18209c65a0f2f0a348aa91c333535305e.yml
-openapi_spec_hash: 0ede81f7fd98f41bc6bbc4167300bfb9
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-01745494602c4845bf7cf84a8f7449d8e53546eb096e3aa6949b89b452c5c90e.yml
+openapi_spec_hash: dd4856e9595fab7a4aaa32a040c3060c
config_hash: 920678668dd2da6f8966fbf1b8fde4e2
From 740b2d42d9e897eefd273779ab2a62c285c3b84f Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Sun, 16 Aug 2026 15:48:58 +0000
Subject: [PATCH 08/11] feat(api): api update
---
.stats.yml | 4 ++--
src/context/dev/types/person_enrich_response.py | 17 +++++++++++++++++
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index f194dd9..2039ac3 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 40
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-01745494602c4845bf7cf84a8f7449d8e53546eb096e3aa6949b89b452c5c90e.yml
-openapi_spec_hash: dd4856e9595fab7a4aaa32a040c3060c
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-c99300223257d60d7614e76585c62cfea40f43df0547db63c952b20c52f209f4.yml
+openapi_spec_hash: 1d4e473f3c566b7a1c3def11b6d84fe0
config_hash: 920678668dd2da6f8966fbf1b8fde4e2
diff --git a/src/context/dev/types/person_enrich_response.py b/src/context/dev/types/person_enrich_response.py
index 34cc571..4b13b5a 100644
--- a/src/context/dev/types/person_enrich_response.py
+++ b/src/context/dev/types/person_enrich_response.py
@@ -163,6 +163,14 @@ class MatchPersonEnrichmentCandidateMatchPersonName(BaseModel):
class MatchPersonEnrichmentCandidateMatchPerson(BaseModel):
+ current_role_status: Literal["present", "none", "unknown"]
+ """Whether the person's current role is known.
+
+ `present` — current_role is populated. `none` — the work history explicitly
+ shows every role has ended. `unknown` — our data sources could not confirm
+ either way; treat a missing current_role as unverified rather than vacant.
+ """
+
education: List[MatchPersonEnrichmentCandidateMatchPersonEducation]
experience: List[MatchPersonEnrichmentCandidateMatchPersonExperience]
@@ -177,10 +185,19 @@ class MatchPersonEnrichmentCandidateMatchPerson(BaseModel):
bio: Optional[str] = None
+ checked_at: Optional[str] = None
+ """When we last refreshed this profile from our data sources (ISO 8601)."""
+
current_role: Optional[MatchPersonEnrichmentCandidateMatchPersonCurrentRole] = None
email: Optional[str] = None
+ last_updated: Optional[str] = None
+ """When the underlying profile data last changed in our data sources (ISO 8601).
+
+ Omitted when unknown.
+ """
+
location: Optional[MatchPersonEnrichmentCandidateMatchPersonLocation] = None
name: Optional[MatchPersonEnrichmentCandidateMatchPersonName] = None
From 253db7d8a9129f323b2a56b82bd5e780bcd8bf86 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 17 Aug 2026 03:52:49 +0000
Subject: [PATCH 09/11] feat(api): api update
---
.stats.yml | 4 +-
src/context/dev/resources/brand.py | 54 +++++++++++++-------
src/context/dev/types/brand_search_params.py | 24 ++++++++-
tests/api_resources/test_brand.py | 6 +++
4 files changed, 65 insertions(+), 23 deletions(-)
diff --git a/.stats.yml b/.stats.yml
index 2039ac3..983602e 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 40
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-c99300223257d60d7614e76585c62cfea40f43df0547db63c952b20c52f209f4.yml
-openapi_spec_hash: 1d4e473f3c566b7a1c3def11b6d84fe0
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-4f279e7b1f0a52f5c917d4256e17188dfe330ddf7495146e2b1611e812605fdc.yml
+openapi_spec_hash: 53a74c4cccbdec8590db38183840a446
config_hash: 920678668dd2da6f8966fbf1b8fde4e2
diff --git a/src/context/dev/resources/brand.py b/src/context/dev/resources/brand.py
index b7bb4ca..5a9f7fd 100644
--- a/src/context/dev/resources/brand.py
+++ b/src/context/dev/resources/brand.py
@@ -2,7 +2,7 @@
from __future__ import annotations
-from typing import Union, Optional
+from typing import List, Union, Optional
from typing_extensions import Literal, overload
import httpx
@@ -1278,7 +1278,10 @@ def search(
self,
*,
query: str,
+ autocomplete: bool | Omit = omit,
+ query_by: List[Literal["name", "domain"]] | Omit = omit,
tags: SequenceNotStr[str] | Omit = omit,
+ typo_tolerance: int | Omit = omit,
# 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,
@@ -1287,23 +1290,25 @@ def search(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> BrandSearchResponse:
"""
- Search brands by name or domain and get back up to 10 lightweight matches
- (domain, name, logo). Name matches rank ahead of domain matches; within each
- group the most popular brands come first: by Tranco rank, then market cap for
- brands outside the Tranco list, with text relevance breaking ties. Matching is
- prefix-based with no typo tolerance, so it is suited to autocomplete. Only
- brands already in the Context.dev index are returned — use /brand/retrieve to
- fetch (and index) a specific domain. Free on Pro and Scale plans; costs 1 credit
- per request on the Free and Starter plans.
+ Search indexed brands by name or domain
Args:
- query: Search term, matched against brand names and domains by prefix (e.g. 'nike',
+ query: Search term, matched against the fields selected by queryBy (e.g. 'nike',
'nike.com', 'nik').
+ autocomplete: Whether the search term matches by prefix, so partial words match as they are
+ typed (e.g. 'nik' matches Nike). Set to false to match whole words only.
+
+ query_by: Fields to match the search term against, as a comma-separated list or repeated
+ parameter: 'name', 'domain', or both. Defaults to both.
+
tags: Optional comma-separated caller-defined tags for tracking this request. Tags are
recorded on the request's usage log and can be used to filter usage on the
dashboard usage page. Up to 20 tags, each 1-50 characters.
+ typo_tolerance: Maximum number of typos tolerated when matching, from 0 to 2. Defaults to 0 (no
+ typo tolerance).
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -1322,7 +1327,10 @@ def search(
query=maybe_transform(
{
"query": query,
+ "autocomplete": autocomplete,
+ "query_by": query_by,
"tags": tags,
+ "typo_tolerance": typo_tolerance,
},
brand_search_params.BrandSearchParams,
),
@@ -2583,7 +2591,10 @@ async def search(
self,
*,
query: str,
+ autocomplete: bool | Omit = omit,
+ query_by: List[Literal["name", "domain"]] | Omit = omit,
tags: SequenceNotStr[str] | Omit = omit,
+ typo_tolerance: int | Omit = omit,
# 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,
@@ -2592,23 +2603,25 @@ async def search(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> BrandSearchResponse:
"""
- Search brands by name or domain and get back up to 10 lightweight matches
- (domain, name, logo). Name matches rank ahead of domain matches; within each
- group the most popular brands come first: by Tranco rank, then market cap for
- brands outside the Tranco list, with text relevance breaking ties. Matching is
- prefix-based with no typo tolerance, so it is suited to autocomplete. Only
- brands already in the Context.dev index are returned — use /brand/retrieve to
- fetch (and index) a specific domain. Free on Pro and Scale plans; costs 1 credit
- per request on the Free and Starter plans.
+ Search indexed brands by name or domain
Args:
- query: Search term, matched against brand names and domains by prefix (e.g. 'nike',
+ query: Search term, matched against the fields selected by queryBy (e.g. 'nike',
'nike.com', 'nik').
+ autocomplete: Whether the search term matches by prefix, so partial words match as they are
+ typed (e.g. 'nik' matches Nike). Set to false to match whole words only.
+
+ query_by: Fields to match the search term against, as a comma-separated list or repeated
+ parameter: 'name', 'domain', or both. Defaults to both.
+
tags: Optional comma-separated caller-defined tags for tracking this request. Tags are
recorded on the request's usage log and can be used to filter usage on the
dashboard usage page. Up to 20 tags, each 1-50 characters.
+ typo_tolerance: Maximum number of typos tolerated when matching, from 0 to 2. Defaults to 0 (no
+ typo tolerance).
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -2627,7 +2640,10 @@ async def search(
query=await async_maybe_transform(
{
"query": query,
+ "autocomplete": autocomplete,
+ "query_by": query_by,
"tags": tags,
+ "typo_tolerance": typo_tolerance,
},
brand_search_params.BrandSearchParams,
),
diff --git a/src/context/dev/types/brand_search_params.py b/src/context/dev/types/brand_search_params.py
index 911d0c5..720c66c 100644
--- a/src/context/dev/types/brand_search_params.py
+++ b/src/context/dev/types/brand_search_params.py
@@ -2,23 +2,43 @@
from __future__ import annotations
-from typing_extensions import Required, TypedDict
+from typing import List
+from typing_extensions import Literal, Required, Annotated, TypedDict
from .._types import SequenceNotStr
+from .._utils import PropertyInfo
__all__ = ["BrandSearchParams"]
class BrandSearchParams(TypedDict, total=False):
query: Required[str]
- """Search term, matched against brand names and domains by prefix (e.g.
+ """Search term, matched against the fields selected by queryBy (e.g.
'nike', 'nike.com', 'nik').
"""
+ autocomplete: bool
+ """
+ Whether the search term matches by prefix, so partial words match as they are
+ typed (e.g. 'nik' matches Nike). Set to false to match whole words only.
+ """
+
+ query_by: Annotated[List[Literal["name", "domain"]], PropertyInfo(alias="queryBy")]
+ """
+ Fields to match the search term against, as a comma-separated list or repeated
+ parameter: 'name', 'domain', or both. Defaults to both.
+ """
+
tags: SequenceNotStr[str]
"""Optional comma-separated caller-defined tags for tracking this request.
Tags are recorded on the request's usage log and can be used to filter usage on
the dashboard usage page. Up to 20 tags, each 1-50 characters.
"""
+
+ typo_tolerance: Annotated[int, PropertyInfo(alias="typoTolerance")]
+ """Maximum number of typos tolerated when matching, from 0 to 2.
+
+ Defaults to 0 (no typo tolerance).
+ """
diff --git a/tests/api_resources/test_brand.py b/tests/api_resources/test_brand.py
index d0912d0..c2321c3 100644
--- a/tests/api_resources/test_brand.py
+++ b/tests/api_resources/test_brand.py
@@ -389,7 +389,10 @@ def test_method_search(self, client: ContextDev) -> None:
def test_method_search_with_all_params(self, client: ContextDev) -> None:
brand = client.brand.search(
query="x",
+ autocomplete=True,
+ query_by=["name"],
tags=["production", "team-alpha"],
+ typo_tolerance=0,
)
assert_matches_type(BrandSearchResponse, brand, path=["response"])
@@ -793,7 +796,10 @@ async def test_method_search(self, async_client: AsyncContextDev) -> None:
async def test_method_search_with_all_params(self, async_client: AsyncContextDev) -> None:
brand = await async_client.brand.search(
query="x",
+ autocomplete=True,
+ query_by=["name"],
tags=["production", "team-alpha"],
+ typo_tolerance=0,
)
assert_matches_type(BrandSearchResponse, brand, path=["response"])
From 4f84467a599b7c80702e95b027e87584ba6d07a7 Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 17 Aug 2026 04:29:51 +0000
Subject: [PATCH 10/11] feat(api): manual updates
---
.stats.yml | 6 +-
api.md | 12 +
src/context/dev/_client.py | 45 +++-
src/context/dev/resources/__init__.py | 14 +
src/context/dev/resources/news.py | 231 ++++++++++++++++
src/context/dev/types/__init__.py | 2 +
src/context/dev/types/news_search_params.py | 253 ++++++++++++++++++
src/context/dev/types/news_search_response.py | 85 ++++++
tests/api_resources/test_news.py | 184 +++++++++++++
9 files changed, 828 insertions(+), 4 deletions(-)
create mode 100644 src/context/dev/resources/news.py
create mode 100644 src/context/dev/types/news_search_params.py
create mode 100644 src/context/dev/types/news_search_response.py
create mode 100644 tests/api_resources/test_news.py
diff --git a/.stats.yml b/.stats.yml
index 983602e..4b9b1cb 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 40
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-4f279e7b1f0a52f5c917d4256e17188dfe330ddf7495146e2b1611e812605fdc.yml
+configured_endpoints: 41
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/context-dev/context.dev-5dbe631ce516ca688273053ddcb58870561bd6dc76fa75df354c954d7af65a3c.yml
openapi_spec_hash: 53a74c4cccbdec8590db38183840a446
-config_hash: 920678668dd2da6f8966fbf1b8fde4e2
+config_hash: 0fb0ceca5946298c416cec0cca5260c7
diff --git a/api.md b/api.md
index 56ac7f5..2a88eee 100644
--- a/api.md
+++ b/api.md
@@ -178,3 +178,15 @@ from context.dev.types import PersonEnrichResponse
Methods:
- client.people.enrich(\*\*params) -> PersonEnrichResponse
+
+# News
+
+Types:
+
+```python
+from context.dev.types import NewsSearchResponse
+```
+
+Methods:
+
+- client.news.search(\*\*params) -> NewsSearchResponse
diff --git a/src/context/dev/_client.py b/src/context/dev/_client.py
index 63888b2..5b83688 100644
--- a/src/context/dev/_client.py
+++ b/src/context/dev/_client.py
@@ -35,9 +35,10 @@
)
if TYPE_CHECKING:
- from .resources import ai, web, batch, brand, parse, people, utility, industry, monitors
+ from .resources import ai, web, news, batch, brand, parse, people, utility, industry, monitors
from .resources.ai import AIResource, AsyncAIResource
from .resources.web import WebResource, AsyncWebResource
+ from .resources.news import NewsResource, AsyncNewsResource
from .resources.batch import BatchResource, AsyncBatchResource
from .resources.brand import BrandResource, AsyncBrandResource
from .resources.parse import ParseResource, AsyncParseResource
@@ -180,6 +181,13 @@ def people(self) -> PeopleResource:
return PeopleResource(self)
+ @cached_property
+ def news(self) -> NewsResource:
+ """Search live first-party RSS and free historical news data by company identity."""
+ from .resources.news import NewsResource
+
+ return NewsResource(self)
+
@cached_property
def with_raw_response(self) -> ContextDevWithRawResponse:
return ContextDevWithRawResponse(self)
@@ -415,6 +423,13 @@ def people(self) -> AsyncPeopleResource:
return AsyncPeopleResource(self)
+ @cached_property
+ def news(self) -> AsyncNewsResource:
+ """Search live first-party RSS and free historical news data by company identity."""
+ from .resources.news import AsyncNewsResource
+
+ return AsyncNewsResource(self)
+
@cached_property
def with_raw_response(self) -> AsyncContextDevWithRawResponse:
return AsyncContextDevWithRawResponse(self)
@@ -592,6 +607,13 @@ def people(self) -> people.PeopleResourceWithRawResponse:
return PeopleResourceWithRawResponse(self._client.people)
+ @cached_property
+ def news(self) -> news.NewsResourceWithRawResponse:
+ """Search live first-party RSS and free historical news data by company identity."""
+ from .resources.news import NewsResourceWithRawResponse
+
+ return NewsResourceWithRawResponse(self._client.news)
+
class AsyncContextDevWithRawResponse:
_client: AsyncContextDev
@@ -657,6 +679,13 @@ def people(self) -> people.AsyncPeopleResourceWithRawResponse:
return AsyncPeopleResourceWithRawResponse(self._client.people)
+ @cached_property
+ def news(self) -> news.AsyncNewsResourceWithRawResponse:
+ """Search live first-party RSS and free historical news data by company identity."""
+ from .resources.news import AsyncNewsResourceWithRawResponse
+
+ return AsyncNewsResourceWithRawResponse(self._client.news)
+
class ContextDevWithStreamedResponse:
_client: ContextDev
@@ -722,6 +751,13 @@ def people(self) -> people.PeopleResourceWithStreamingResponse:
return PeopleResourceWithStreamingResponse(self._client.people)
+ @cached_property
+ def news(self) -> news.NewsResourceWithStreamingResponse:
+ """Search live first-party RSS and free historical news data by company identity."""
+ from .resources.news import NewsResourceWithStreamingResponse
+
+ return NewsResourceWithStreamingResponse(self._client.news)
+
class AsyncContextDevWithStreamedResponse:
_client: AsyncContextDev
@@ -787,6 +823,13 @@ def people(self) -> people.AsyncPeopleResourceWithStreamingResponse:
return AsyncPeopleResourceWithStreamingResponse(self._client.people)
+ @cached_property
+ def news(self) -> news.AsyncNewsResourceWithStreamingResponse:
+ """Search live first-party RSS and free historical news data by company identity."""
+ from .resources.news import AsyncNewsResourceWithStreamingResponse
+
+ return AsyncNewsResourceWithStreamingResponse(self._client.news)
+
Client = ContextDev
diff --git a/src/context/dev/resources/__init__.py b/src/context/dev/resources/__init__.py
index 9d68085..03f6108 100644
--- a/src/context/dev/resources/__init__.py
+++ b/src/context/dev/resources/__init__.py
@@ -16,6 +16,14 @@
WebResourceWithStreamingResponse,
AsyncWebResourceWithStreamingResponse,
)
+from .news import (
+ NewsResource,
+ AsyncNewsResource,
+ NewsResourceWithRawResponse,
+ AsyncNewsResourceWithRawResponse,
+ NewsResourceWithStreamingResponse,
+ AsyncNewsResourceWithStreamingResponse,
+)
from .batch import (
BatchResource,
AsyncBatchResource,
@@ -128,4 +136,10 @@
"AsyncPeopleResourceWithRawResponse",
"PeopleResourceWithStreamingResponse",
"AsyncPeopleResourceWithStreamingResponse",
+ "NewsResource",
+ "AsyncNewsResource",
+ "NewsResourceWithRawResponse",
+ "AsyncNewsResourceWithRawResponse",
+ "NewsResourceWithStreamingResponse",
+ "AsyncNewsResourceWithStreamingResponse",
]
diff --git a/src/context/dev/resources/news.py b/src/context/dev/resources/news.py
new file mode 100644
index 0000000..607b2c2
--- /dev/null
+++ b/src/context/dev/resources/news.py
@@ -0,0 +1,231 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+
+import httpx
+
+from ..types import news_search_params
+from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
+from .._utils import maybe_transform, async_maybe_transform
+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
+from ..types.news_search_response import NewsSearchResponse
+
+__all__ = ["NewsResource", "AsyncNewsResource"]
+
+
+class NewsResource(SyncAPIResource):
+ """Search live first-party RSS and free historical news data by company identity."""
+
+ @cached_property
+ def with_raw_response(self) -> NewsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/context-dot-dev/context-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return NewsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> NewsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/context-dot-dev/context-python-sdk#with_streaming_response
+ """
+ return NewsResourceWithStreamingResponse(self)
+
+ def search(
+ self,
+ *,
+ search_by: news_search_params.SearchBy,
+ cursor: Optional[str] | Omit = omit,
+ filter_by: news_search_params.FilterBy | Omit = omit,
+ limit: int | Omit = omit,
+ sort_by: news_search_params.SortBy | Omit = omit,
+ tags: SequenceNotStr[str] | Omit = omit,
+ # 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,
+ ) -> NewsSearchResponse:
+ """
+ Searches live and historical company news for one company, identified in
+ searchBy by name, domain, ticker (optionally disambiguated by exchange), or
+ ISIN. Results can be filtered by publisher domain, publisher country, article
+ language, article type, and published-at date, and include stable story IDs,
+ source metadata, verified entity relevance, and cursor pagination.
+
+ Args:
+ search_by: What to search for.
+
+ cursor: Opaque next_cursor from the previous response, or null for the first page.
+
+ filter_by: Optional result filters.
+
+ limit: Maximum results to return. Defaults to 10.
+
+ sort_by: Result ordering. Defaults to newest.
+
+ tags: Optional tags for tracking usage. Up to 20 tags, each 1 to 50 characters.
+
+ 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
+ """
+ return self._post(
+ "/news/search",
+ body=maybe_transform(
+ {
+ "search_by": search_by,
+ "cursor": cursor,
+ "filter_by": filter_by,
+ "limit": limit,
+ "sort_by": sort_by,
+ "tags": tags,
+ },
+ news_search_params.NewsSearchParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NewsSearchResponse,
+ )
+
+
+class AsyncNewsResource(AsyncAPIResource):
+ """Search live first-party RSS and free historical news data by company identity."""
+
+ @cached_property
+ def with_raw_response(self) -> AsyncNewsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/context-dot-dev/context-python-sdk#accessing-raw-response-data-eg-headers
+ """
+ return AsyncNewsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncNewsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/context-dot-dev/context-python-sdk#with_streaming_response
+ """
+ return AsyncNewsResourceWithStreamingResponse(self)
+
+ async def search(
+ self,
+ *,
+ search_by: news_search_params.SearchBy,
+ cursor: Optional[str] | Omit = omit,
+ filter_by: news_search_params.FilterBy | Omit = omit,
+ limit: int | Omit = omit,
+ sort_by: news_search_params.SortBy | Omit = omit,
+ tags: SequenceNotStr[str] | Omit = omit,
+ # 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,
+ ) -> NewsSearchResponse:
+ """
+ Searches live and historical company news for one company, identified in
+ searchBy by name, domain, ticker (optionally disambiguated by exchange), or
+ ISIN. Results can be filtered by publisher domain, publisher country, article
+ language, article type, and published-at date, and include stable story IDs,
+ source metadata, verified entity relevance, and cursor pagination.
+
+ Args:
+ search_by: What to search for.
+
+ cursor: Opaque next_cursor from the previous response, or null for the first page.
+
+ filter_by: Optional result filters.
+
+ limit: Maximum results to return. Defaults to 10.
+
+ sort_by: Result ordering. Defaults to newest.
+
+ tags: Optional tags for tracking usage. Up to 20 tags, each 1 to 50 characters.
+
+ 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
+ """
+ return await self._post(
+ "/news/search",
+ body=await async_maybe_transform(
+ {
+ "search_by": search_by,
+ "cursor": cursor,
+ "filter_by": filter_by,
+ "limit": limit,
+ "sort_by": sort_by,
+ "tags": tags,
+ },
+ news_search_params.NewsSearchParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=NewsSearchResponse,
+ )
+
+
+class NewsResourceWithRawResponse:
+ def __init__(self, news: NewsResource) -> None:
+ self._news = news
+
+ self.search = to_raw_response_wrapper(
+ news.search,
+ )
+
+
+class AsyncNewsResourceWithRawResponse:
+ def __init__(self, news: AsyncNewsResource) -> None:
+ self._news = news
+
+ self.search = async_to_raw_response_wrapper(
+ news.search,
+ )
+
+
+class NewsResourceWithStreamingResponse:
+ def __init__(self, news: NewsResource) -> None:
+ self._news = news
+
+ self.search = to_streamed_response_wrapper(
+ news.search,
+ )
+
+
+class AsyncNewsResourceWithStreamingResponse:
+ def __init__(self, news: AsyncNewsResource) -> None:
+ self._news = news
+
+ self.search = async_to_streamed_response_wrapper(
+ news.search,
+ )
diff --git a/src/context/dev/types/__init__.py b/src/context/dev/types/__init__.py
index ca25937..139c5e1 100644
--- a/src/context/dev/types/__init__.py
+++ b/src/context/dev/types/__init__.py
@@ -9,6 +9,7 @@
from .webhook_delivery import WebhookDelivery as WebhookDelivery
from .batch_list_params import BatchListParams as BatchListParams
from .web_search_params import WebSearchParams as WebSearchParams
+from .news_search_params import NewsSearchParams as NewsSearchParams
from .web_extract_params import WebExtractParams as WebExtractParams
from .batch_list_response import BatchListResponse as BatchListResponse
from .batch_submit_params import BatchSubmitParams as BatchSubmitParams
@@ -17,6 +18,7 @@
from .parse_handle_params import ParseHandleParams as ParseHandleParams
from .web_search_response import WebSearchResponse as WebSearchResponse
from .monitor_run_response import MonitorRunResponse as MonitorRunResponse
+from .news_search_response import NewsSearchResponse as NewsSearchResponse
from .person_enrich_params import PersonEnrichParams as PersonEnrichParams
from .web_extract_response import WebExtractResponse as WebExtractResponse
from .batch_cancel_response import BatchCancelResponse as BatchCancelResponse
diff --git a/src/context/dev/types/news_search_params.py b/src/context/dev/types/news_search_params.py
new file mode 100644
index 0000000..a7136e6
--- /dev/null
+++ b/src/context/dev/types/news_search_params.py
@@ -0,0 +1,253 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict
+
+from .._types import SequenceNotStr
+from .._utils import PropertyInfo
+
+__all__ = [
+ "NewsSearchParams",
+ "SearchBy",
+ "SearchByEntity",
+ "SearchByEntityNewsSearchEntityByName",
+ "SearchByEntityNewsSearchEntityByDomain",
+ "SearchByEntityNewsSearchEntityByTicker",
+ "SearchByEntityNewsSearchEntityByIsin",
+ "FilterBy",
+ "FilterByDate",
+ "SortBy",
+]
+
+
+class NewsSearchParams(TypedDict, total=False):
+ search_by: Required[Annotated[SearchBy, PropertyInfo(alias="searchBy")]]
+ """What to search for."""
+
+ cursor: Optional[str]
+ """Opaque next_cursor from the previous response, or null for the first page."""
+
+ filter_by: Annotated[FilterBy, PropertyInfo(alias="filterBy")]
+ """Optional result filters."""
+
+ limit: int
+ """Maximum results to return. Defaults to 10."""
+
+ sort_by: Annotated[SortBy, PropertyInfo(alias="sortBy")]
+ """Result ordering. Defaults to newest."""
+
+ tags: SequenceNotStr[str]
+ """Optional tags for tracking usage. Up to 20 tags, each 1 to 50 characters."""
+
+
+class SearchByEntityNewsSearchEntityByName(TypedDict, total=False):
+ """Identify the company by name."""
+
+ name: Required[str]
+ """Company name."""
+
+ type: Required[Literal["name"]]
+
+
+class SearchByEntityNewsSearchEntityByDomain(TypedDict, total=False):
+ """Identify the company by website domain."""
+
+ domain: Required[str]
+ """Company website domain, such as apple.com."""
+
+ type: Required[Literal["domain"]]
+
+
+class SearchByEntityNewsSearchEntityByTicker(TypedDict, total=False):
+ """Identify the company by stock ticker, optionally scoped to an exchange."""
+
+ ticker: Required[str]
+ """Public-company ticker."""
+
+ type: Required[Literal["ticker"]]
+
+ exchange: Literal[
+ "AMEX",
+ "AMS",
+ "AQS",
+ "ASX",
+ "ATH",
+ "BER",
+ "BME",
+ "BRU",
+ "BSE",
+ "BUD",
+ "BUE",
+ "BVC",
+ "CBOE",
+ "CNQ",
+ "CPH",
+ "DFM",
+ "DOH",
+ "DUB",
+ "DUS",
+ "DXE",
+ "EGX",
+ "FSX",
+ "HAM",
+ "HEL",
+ "HKSE",
+ "HOSE",
+ "ICE",
+ "IOB",
+ "IST",
+ "JKT",
+ "JNB",
+ "JPX",
+ "KLS",
+ "KOE",
+ "KSC",
+ "KUW",
+ "LIS",
+ "LSE",
+ "MCX",
+ "MEX",
+ "MIL",
+ "MUN",
+ "NASDAQ",
+ "NEO",
+ "NSE",
+ "NYSE",
+ "NZE",
+ "OSL",
+ "OTC",
+ "PAR",
+ "PNK",
+ "PRA",
+ "RIS",
+ "SAO",
+ "SAU",
+ "SES",
+ "SET",
+ "SGO",
+ "SHH",
+ "SHZ",
+ "SIX",
+ "STO",
+ "STU",
+ "TAI",
+ "TAL",
+ "TLV",
+ "TSX",
+ "TSXV",
+ "TWO",
+ "VIE",
+ "WSE",
+ "XETRA",
+ ]
+ """
+ Stock exchange the ticker trades on, used to disambiguate tickers listed on
+ multiple exchanges.
+ """
+
+
+class SearchByEntityNewsSearchEntityByIsin(TypedDict, total=False):
+ """Identify the company by International Securities Identification Number."""
+
+ isin: Required[str]
+ """International Securities Identification Number."""
+
+ type: Required[Literal["isin"]]
+
+
+SearchByEntity: TypeAlias = Union[
+ SearchByEntityNewsSearchEntityByName,
+ SearchByEntityNewsSearchEntityByDomain,
+ SearchByEntityNewsSearchEntityByTicker,
+ SearchByEntityNewsSearchEntityByIsin,
+]
+
+
+class SearchBy(TypedDict, total=False):
+ """What to search for."""
+
+ entity: Required[SearchByEntity]
+ """The company to search news for, identified by name, domain, ticker, or ISIN."""
+
+ type: Required[Literal["entity"]]
+ """How to search. Only entity search is supported."""
+
+
+_FilterByDateReservedKeywords = TypedDict(
+ "_FilterByDateReservedKeywords",
+ {
+ "from": int,
+ },
+ total=False,
+)
+
+
+class FilterByDate(_FilterByDateReservedKeywords, total=False):
+ """Published-at window in epoch milliseconds."""
+
+ to: int
+ """Inclusive end of the published-at window, in epoch milliseconds."""
+
+
+class FilterBy(TypedDict, total=False):
+ """Optional result filters."""
+
+ article_language: Annotated[
+ List[Literal["ar", "de", "en", "es", "fr", "hi", "it", "ja", "ko", "nl", "pt", "ru", "zh"]],
+ PropertyInfo(alias="articleLanguage"),
+ ]
+ """Article languages to include. Up to 3."""
+
+ article_type: Annotated[
+ List[Literal["editorial", "press_release", "regulatory_filing", "advisory"]], PropertyInfo(alias="articleType")
+ ]
+ """Article types to include. Up to 3."""
+
+ date: FilterByDate
+ """Published-at window in epoch milliseconds."""
+
+ source_country: Annotated[
+ List[
+ Literal[
+ "ae",
+ "ar",
+ "au",
+ "ca",
+ "cg",
+ "ch",
+ "cl",
+ "de",
+ "fi",
+ "fr",
+ "gb",
+ "hk",
+ "il",
+ "in",
+ "jp",
+ "kr",
+ "mx",
+ "ng",
+ "nl",
+ "qa",
+ "sa",
+ "se",
+ "sg",
+ "us",
+ "za",
+ ]
+ ],
+ PropertyInfo(alias="sourceCountry"),
+ ]
+ """Publisher countries to include, as lowercase ISO 3166-1 alpha-2 codes. Up to 3."""
+
+ source_domain: Annotated[SequenceNotStr[str], PropertyInfo(alias="sourceDomain")]
+ """Publisher domains to include. Up to 3."""
+
+
+class SortBy(TypedDict, total=False):
+ """Result ordering. Defaults to newest."""
+
+ type: Required[Literal["relevance", "newest"]]
+ """Result ordering."""
diff --git a/src/context/dev/types/news_search_response.py b/src/context/dev/types/news_search_response.py
new file mode 100644
index 0000000..af82932
--- /dev/null
+++ b/src/context/dev/types/news_search_response.py
@@ -0,0 +1,85 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["NewsSearchResponse", "Data", "DataMatch", "DataSource", "Meta", "KeyMetadata"]
+
+
+class DataMatch(BaseModel):
+ confidence: Optional[float] = None
+
+ level: Literal["primary", "secondary"]
+
+
+class DataSource(BaseModel):
+ direct: bool
+ """True when Context observed this article in the publisher-owned feed."""
+
+ domain: str
+
+ name: str
+
+
+class Data(BaseModel):
+ id: str
+
+ authors: List[str]
+
+ description: Optional[str] = None
+
+ image_url: Optional[str] = None
+
+ language: Optional[str] = None
+
+ match: DataMatch
+
+ published_at: Optional[datetime] = None
+
+ source: DataSource
+
+ story_id: str
+ """Groups matching normalized headlines published on the same UTC day."""
+
+ title: str
+
+ type: Literal["editorial", "press_release", "regulatory_filing", "advisory"]
+
+ url: str
+
+
+class Meta(BaseModel):
+ count: int
+
+
+class KeyMetadata(BaseModel):
+ """Metadata about the API key used for the request.
+
+ Included in every response whenever a valid API key is provided, even when the response status is not 200.
+ """
+
+ credits_consumed: int
+ """The number of credits consumed by this request."""
+
+ credits_remaining: int
+ """The number of credits remaining for your organization after this request."""
+
+
+class NewsSearchResponse(BaseModel):
+ data: List[Data]
+
+ has_more: bool
+
+ meta: Meta
+
+ next_cursor: Optional[str] = None
+
+ key_metadata: Optional[KeyMetadata] = None
+ """Metadata about the API key used for the request.
+
+ Included in every response whenever a valid API key is provided, even when the
+ response status is not 200.
+ """
diff --git a/tests/api_resources/test_news.py b/tests/api_resources/test_news.py
new file mode 100644
index 0000000..16c0a1d
--- /dev/null
+++ b/tests/api_resources/test_news.py
@@ -0,0 +1,184 @@
+# 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 context.dev import ContextDev, AsyncContextDev
+from tests.utils import assert_matches_type
+from context.dev.types import NewsSearchResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestNews:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_search(self, client: ContextDev) -> None:
+ news = client.news.search(
+ search_by={
+ "entity": {
+ "name": "xx",
+ "type": "name",
+ },
+ "type": "entity",
+ },
+ )
+ assert_matches_type(NewsSearchResponse, news, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_method_search_with_all_params(self, client: ContextDev) -> None:
+ news = client.news.search(
+ search_by={
+ "entity": {
+ "name": "xx",
+ "type": "name",
+ },
+ "type": "entity",
+ },
+ cursor="cursor",
+ filter_by={
+ "article_language": ["ar"],
+ "article_type": ["editorial"],
+ "date": {
+ "from": 0,
+ "to": 0,
+ },
+ "source_country": ["ae"],
+ "source_domain": ["x"],
+ },
+ limit=1,
+ sort_by={"type": "relevance"},
+ tags=["production", "team-alpha"],
+ )
+ assert_matches_type(NewsSearchResponse, news, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_raw_response_search(self, client: ContextDev) -> None:
+ response = client.news.with_raw_response.search(
+ search_by={
+ "entity": {
+ "name": "xx",
+ "type": "name",
+ },
+ "type": "entity",
+ },
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ news = response.parse()
+ assert_matches_type(NewsSearchResponse, news, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ def test_streaming_response_search(self, client: ContextDev) -> None:
+ with client.news.with_streaming_response.search(
+ search_by={
+ "entity": {
+ "name": "xx",
+ "type": "name",
+ },
+ "type": "entity",
+ },
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ news = response.parse()
+ assert_matches_type(NewsSearchResponse, news, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncNews:
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_search(self, async_client: AsyncContextDev) -> None:
+ news = await async_client.news.search(
+ search_by={
+ "entity": {
+ "name": "xx",
+ "type": "name",
+ },
+ "type": "entity",
+ },
+ )
+ assert_matches_type(NewsSearchResponse, news, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_method_search_with_all_params(self, async_client: AsyncContextDev) -> None:
+ news = await async_client.news.search(
+ search_by={
+ "entity": {
+ "name": "xx",
+ "type": "name",
+ },
+ "type": "entity",
+ },
+ cursor="cursor",
+ filter_by={
+ "article_language": ["ar"],
+ "article_type": ["editorial"],
+ "date": {
+ "from": 0,
+ "to": 0,
+ },
+ "source_country": ["ae"],
+ "source_domain": ["x"],
+ },
+ limit=1,
+ sort_by={"type": "relevance"},
+ tags=["production", "team-alpha"],
+ )
+ assert_matches_type(NewsSearchResponse, news, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_raw_response_search(self, async_client: AsyncContextDev) -> None:
+ response = await async_client.news.with_raw_response.search(
+ search_by={
+ "entity": {
+ "name": "xx",
+ "type": "name",
+ },
+ "type": "entity",
+ },
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ news = await response.parse()
+ assert_matches_type(NewsSearchResponse, news, path=["response"])
+
+ @pytest.mark.skip(reason="Mock server tests are disabled")
+ @parametrize
+ async def test_streaming_response_search(self, async_client: AsyncContextDev) -> None:
+ async with async_client.news.with_streaming_response.search(
+ search_by={
+ "entity": {
+ "name": "xx",
+ "type": "name",
+ },
+ "type": "entity",
+ },
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ news = await response.parse()
+ assert_matches_type(NewsSearchResponse, news, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
From 63aeaedd29f550d9615e5b7a3b59c4109204e3ae Mon Sep 17 00:00:00 2001
From: "stainless-app[bot]"
<142633134+stainless-app[bot]@users.noreply.github.com>
Date: Mon, 17 Aug 2026 04:30:16 +0000
Subject: [PATCH 11/11] release: 2.10.0
---
.release-please-manifest.json | 2 +-
CHANGELOG.md | 13 +++++++++++++
pyproject.toml | 2 +-
src/context/dev/_version.py | 2 +-
4 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 427b8ec..21f6056 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "2.9.0"
+ ".": "2.10.0"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9e5e281..b8cde85 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,18 @@
# Changelog
+## 2.10.0 (2026-08-17)
+
+Full Changelog: [v2.9.0...v2.10.0](https://github.com/context-dot-dev/context-python-sdk/compare/v2.9.0...v2.10.0)
+
+### Features
+
+* **api:** api update ([253db7d](https://github.com/context-dot-dev/context-python-sdk/commit/253db7d8a9129f323b2a56b82bd5e780bcd8bf86))
+* **api:** api update ([740b2d4](https://github.com/context-dot-dev/context-python-sdk/commit/740b2d42d9e897eefd273779ab2a62c285c3b84f))
+* **api:** api update ([07e8016](https://github.com/context-dot-dev/context-python-sdk/commit/07e80168ee24305f42bfac338ee85d676e9601d7))
+* **api:** api update ([499fb3f](https://github.com/context-dot-dev/context-python-sdk/commit/499fb3fdef35838f9c631560b2bc073b73f3a04a))
+* **api:** api update ([853f56b](https://github.com/context-dot-dev/context-python-sdk/commit/853f56b503807b1bb41f4ca26800b56fc41e395c))
+* **api:** manual updates ([4f84467](https://github.com/context-dot-dev/context-python-sdk/commit/4f84467a599b7c80702e95b027e87584ba6d07a7))
+
## 2.9.0 (2026-08-07)
Full Changelog: [v2.8.0...v2.9.0](https://github.com/context-dot-dev/context-python-sdk/compare/v2.8.0...v2.9.0)
diff --git a/pyproject.toml b/pyproject.toml
index 7c3d73f..5d891ed 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "context.dev"
-version = "2.9.0"
+version = "2.10.0"
description = "The official Python library for the context.dev API"
dynamic = ["readme"]
license = "Apache-2.0"
diff --git a/src/context/dev/_version.py b/src/context/dev/_version.py
index 593f642..9acee71 100644
--- a/src/context/dev/_version.py
+++ b/src/context/dev/_version.py
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
__title__ = "context.dev"
-__version__ = "2.9.0" # x-release-please-version
+__version__ = "2.10.0" # x-release-please-version