diff --git a/.fern/metadata.json b/.fern/metadata.json
index 2239b83..0ff0268 100644
--- a/.fern/metadata.json
+++ b/.fern/metadata.json
@@ -1,7 +1,7 @@
{
- "cliVersion": "5.45.3",
+ "cliVersion": "5.50.6",
"generatorName": "fernapi/fern-python-sdk",
- "generatorVersion": "5.14.12",
+ "generatorVersion": "5.14.21",
"generatorConfig": {
"package_name": "newscatcher_catchall",
"client": {
@@ -9,10 +9,10 @@
"filename": "client.py"
}
},
- "originGitCommit": "56ea60da11c1b8d90274a137f5fbc3bb89b9fac4",
+ "originGitCommit": "f9a7ba07dda24e9d9936bb543686bc542355479e",
"originGitCommitIsDirty": true,
"invokedBy": "ci",
"requestedVersion": "AUTO",
"ciProvider": "unknown",
- "sdkVersion": "3.0.1"
+ "sdkVersion": "0.0.0.dev0"
}
\ No newline at end of file
diff --git a/.fern/replay.lock b/.fern/replay.lock
index cae5c71..f04ebfb 100644
--- a/.fern/replay.lock
+++ b/.fern/replay.lock
@@ -6,5 +6,11 @@ generations:
timestamp: 2026-06-10T12:28:53.089Z
cli_version: unknown
generator_versions: {}
-current_generation: 045deff8d2bc498634f39b6baaea0778b69bfecd
+ - commit_sha: e6179f9ae1194808b56ce70363413efa8fb470f4
+ tree_hash: 7c9cb6f2e645434ac16a824fbb05bb2b02af1074
+ timestamp: 2026-06-24T15:56:21.929Z
+ cli_version: unknown
+ generator_versions:
+ fernapi/fern-python-sdk: 5.14.21
+current_generation: e6179f9ae1194808b56ce70363413efa8fb470f4
patches: []
diff --git a/README.md b/README.md
index 78d5917..e2f2fc4 100644
--- a/README.md
+++ b/README.md
@@ -218,3 +218,4 @@ On the other hand, contributions to the README are always very welcome!
+
diff --git a/poetry.lock b/poetry.lock
index c7aad2a..bc07681 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -188,14 +188,14 @@ files = [
[[package]]
name = "anyio"
-version = "4.13.0"
+version = "4.14.0"
description = "High-level concurrency and networking framework on top of asyncio or Trio"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
- {file = "anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708"},
- {file = "anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc"},
+ {file = "anyio-4.14.0-py3-none-any.whl", hash = "sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9"},
+ {file = "anyio-4.14.0.tar.gz", hash = "sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89"},
]
[package.dependencies]
@@ -247,14 +247,14 @@ files = [
[[package]]
name = "certifi"
-version = "2026.5.20"
+version = "2026.6.17"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
- {file = "certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897"},
- {file = "certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d"},
+ {file = "certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db"},
+ {file = "certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432"},
]
[[package]]
@@ -1106,14 +1106,14 @@ windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pytest"
-version = "9.0.3"
+version = "9.1.1"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
- {file = "pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9"},
- {file = "pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c"},
+ {file = "pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c"},
+ {file = "pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313"},
]
[package.dependencies]
@@ -1466,4 +1466,4 @@ aiohttp = ["aiohttp", "httpx-aiohttp"]
[metadata]
lock-version = "2.1"
python-versions = "^3.10"
-content-hash = "8a2af44ec07cc26b89a775d8383a2fbf2203875bc97f422dec79ecd475a30518"
+content-hash = "ae72458cc9170491280c32063e114504d6d14e4e72794aefcc730f6d72478732"
diff --git a/pyproject.toml b/pyproject.toml
index 1dfc9fd..c6579e4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ dynamic = ["version"]
[tool.poetry]
name = "newscatcher-catchall-sdk"
-version = "3.0.1"
+version = "0.0.0.dev0"
description = ""
readme = "README.md"
authors = []
@@ -38,7 +38,7 @@ Repository = 'https://github.com/Newscatcher/newscatcher-catchall-python'
[tool.poetry.dependencies]
python = "^3.10"
-aiohttp = { version = ">=3.14.0,<4", optional = true, python = ">=3.10"}
+aiohttp = { version = ">=3.14.1,<4", optional = true, python = ">=3.10"}
httpx = ">=0.21.2"
httpx-aiohttp = { version = "0.1.8", optional = true, python = ">=3.10"}
pydantic = ">= 1.9.2"
@@ -58,6 +58,7 @@ ruff = "==0.11.5"
[tool.pytest.ini_options]
testpaths = [ "tests" ]
asyncio_mode = "auto"
+norecursedirs = [ "src" ]
markers = [
"aiohttp: tests that require httpx_aiohttp to be installed",
]
diff --git a/reference.md b/reference.md
index 53aba90..cd104dc 100644
--- a/reference.md
+++ b/reference.md
@@ -651,6 +651,79 @@ client.jobs.get_job_results(
+
+
+
+
+client.jobs.get_job_results_csv(...) -> str
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Returns a completed job's result records as a CSV download. One row per record, with enrichment fields as columns, citations as a JSON column, and connected entities split into `event_associated_entities` and `mention_entities` JSON columns.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```python
+from newscatcher_catchall import CatchAllApi
+from newscatcher_catchall.environment import CatchAllApiEnvironment
+
+client = CatchAllApi(
+ api_key="",
+ environment=CatchAllApiEnvironment.DEFAULT,
+)
+
+client.jobs.get_job_results_csv(
+ job_id="job_id",
+)
+
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**job_id:** `str` — Unique job identifier returned from [`POST /catchAll/submit`](https://www.newscatcherapi.com/docs/web-search-api/api-reference/jobs/create-job).
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+
+
+
+
+
@@ -1134,6 +1207,79 @@ client.monitors.pull_monitor_results(
+
+
+
+
+client.monitors.pull_monitor_results_csv(...) -> str
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Returns the most recent run's records as a CSV download. One row per record, with enrichment fields as columns, citations as a JSON column, and connected entities split into `event_associated_entities` and `mention_entities` JSON columns.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```python
+from newscatcher_catchall import CatchAllApi
+from newscatcher_catchall.environment import CatchAllApiEnvironment
+
+client = CatchAllApi(
+ api_key="",
+ environment=CatchAllApiEnvironment.DEFAULT,
+)
+
+client.monitors.pull_monitor_results_csv(
+ monitor_id="monitor_id",
+)
+
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**monitor_id:** `str` — Monitor identifier.
+
+
+
+
+
+-
+
+**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
+
+
+
+
+
@@ -3312,6 +3458,14 @@ client.entities.update_entity(
-
+**external_entity_id:** `typing.Optional[str]` — Updated external identifier for this entity.
+
+
+
+
+
+-
+
**additional_attributes:** `typing.Optional[AdditionalAttributes]`
diff --git a/src/newscatcher_catchall/__init__.py b/src/newscatcher_catchall/__init__.py
index 3a2cea4..b217417 100644
--- a/src/newscatcher_catchall/__init__.py
+++ b/src/newscatcher_catchall/__init__.py
@@ -21,6 +21,7 @@
CompanyEnrichmentOutput,
CompanyEnrichmentOutputMetadata,
CompanyEnrichmentOutputMetadataDomainUrlConfidence,
+ ConnectedDataset,
ConnectedEntity,
Context,
ContinueResponseDto,
@@ -173,6 +174,7 @@
"CompanyEnrichmentOutput": ".types",
"CompanyEnrichmentOutputMetadata": ".types",
"CompanyEnrichmentOutputMetadataDomainUrlConfidence": ".types",
+ "ConnectedDataset": ".types",
"ConnectedEntity": ".types",
"Context": ".types",
"ContinueResponseDto": ".types",
@@ -353,6 +355,7 @@ def __dir__():
"CompanyEnrichmentOutput",
"CompanyEnrichmentOutputMetadata",
"CompanyEnrichmentOutputMetadataDomainUrlConfidence",
+ "ConnectedDataset",
"ConnectedEntity",
"Context",
"ContinueResponseDto",
diff --git a/src/newscatcher_catchall/core/client_wrapper.py b/src/newscatcher_catchall/core/client_wrapper.py
index 24f6fe8..4841652 100644
--- a/src/newscatcher_catchall/core/client_wrapper.py
+++ b/src/newscatcher_catchall/core/client_wrapper.py
@@ -29,12 +29,12 @@ def get_headers(self) -> typing.Dict[str, str]:
import platform
headers: typing.Dict[str, str] = {
- "User-Agent": "newscatcher-catchall-sdk/3.0.1",
+ "User-Agent": "newscatcher-catchall-sdk/0.0.0.dev0",
"X-Fern-Language": "Python",
"X-Fern-Runtime": f"python/{platform.python_version()}",
"X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}",
"X-Fern-SDK-Name": "newscatcher-catchall-sdk",
- "X-Fern-SDK-Version": "3.0.1",
+ "X-Fern-SDK-Version": "0.0.0.dev0",
**(self.get_custom_headers() or {}),
}
headers["x-api-key"] = self.api_key
diff --git a/src/newscatcher_catchall/core/http_sse/_api.py b/src/newscatcher_catchall/core/http_sse/_api.py
index b375399..fd13730 100644
--- a/src/newscatcher_catchall/core/http_sse/_api.py
+++ b/src/newscatcher_catchall/core/http_sse/_api.py
@@ -10,6 +10,8 @@
from ._exceptions import SSEError
from ._models import ServerSentEvent
+MAX_LINE_SIZE: int = 1_048_576 # 1 MiB
+
class EventSource:
def __init__(self, response: httpx.Response) -> None:
@@ -75,10 +77,20 @@ def iter_sse(self) -> Iterator[ServerSentEvent]:
if sse is not None:
yield sse
+ if len(buf) > MAX_LINE_SIZE:
+ raise SSEError(
+ f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline"
+ )
+
# Flush any remaining bytes from the incremental decoder
buf += text_decoder.decode(b"", final=True)
buf = buf.replace("\r\n", "\n").replace("\r", "\n")
+ if len(buf) > MAX_LINE_SIZE:
+ raise SSEError(
+ f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline"
+ )
+
while "\n" in buf:
line, buf = buf.split("\n", 1)
sse = decoder.decode(line)
@@ -107,10 +119,20 @@ async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]:
if sse is not None:
yield sse
+ if len(buf) > MAX_LINE_SIZE:
+ raise SSEError(
+ f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline"
+ )
+
# Flush any remaining bytes from the incremental decoder
buf += text_decoder.decode(b"", final=True)
buf = buf.replace("\r\n", "\n").replace("\r", "\n")
+ if len(buf) > MAX_LINE_SIZE:
+ raise SSEError(
+ f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline"
+ )
+
while "\n" in buf:
line, buf = buf.split("\n", 1)
sse = decoder.decode(line)
diff --git a/src/newscatcher_catchall/entities/client.py b/src/newscatcher_catchall/entities/client.py
index 22cfea7..7d1f12d 100644
--- a/src/newscatcher_catchall/entities/client.py
+++ b/src/newscatcher_catchall/entities/client.py
@@ -106,6 +106,7 @@ def create_entity(
name: str,
entity_type: typing.Optional[EntityType] = OMIT,
description: typing.Optional[str] = OMIT,
+ external_entity_id: typing.Optional[str] = OMIT,
additional_attributes: typing.Optional[AdditionalAttributes] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
) -> CreateEntityResponse:
@@ -127,6 +128,9 @@ def create_entity(
description : typing.Optional[str]
Free-text description of the entity used for disambiguation when similar names exist.
+ external_entity_id : typing.Optional[str]
+ Optional external identifier for this entity. Free-form string, not enforced as unique. Use it to store your own CRM, data warehouse, or internal database ID so you can join CatchAll results back to your systems.
+
additional_attributes : typing.Optional[AdditionalAttributes]
request_options : typing.Optional[RequestOptions]
@@ -165,6 +169,7 @@ def create_entity(
name=name,
entity_type=entity_type,
description=description,
+ external_entity_id=external_entity_id,
additional_attributes=additional_attributes,
request_options=request_options,
)
@@ -303,6 +308,7 @@ def update_entity(
*,
name: typing.Optional[str] = OMIT,
description: typing.Optional[str] = OMIT,
+ external_entity_id: typing.Optional[str] = OMIT,
additional_attributes: typing.Optional[AdditionalAttributes] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
) -> EntityResponse:
@@ -320,6 +326,9 @@ def update_entity(
description : typing.Optional[str]
Updated description.
+ external_entity_id : typing.Optional[str]
+ Updated external identifier for this entity.
+
additional_attributes : typing.Optional[AdditionalAttributes]
request_options : typing.Optional[RequestOptions]
@@ -355,6 +364,7 @@ def update_entity(
entity_id,
name=name,
description=description,
+ external_entity_id=external_entity_id,
additional_attributes=additional_attributes,
request_options=request_options,
)
@@ -455,6 +465,7 @@ async def create_entity(
name: str,
entity_type: typing.Optional[EntityType] = OMIT,
description: typing.Optional[str] = OMIT,
+ external_entity_id: typing.Optional[str] = OMIT,
additional_attributes: typing.Optional[AdditionalAttributes] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
) -> CreateEntityResponse:
@@ -476,6 +487,9 @@ async def create_entity(
description : typing.Optional[str]
Free-text description of the entity used for disambiguation when similar names exist.
+ external_entity_id : typing.Optional[str]
+ Optional external identifier for this entity. Free-form string, not enforced as unique. Use it to store your own CRM, data warehouse, or internal database ID so you can join CatchAll results back to your systems.
+
additional_attributes : typing.Optional[AdditionalAttributes]
request_options : typing.Optional[RequestOptions]
@@ -522,6 +536,7 @@ async def main() -> None:
name=name,
entity_type=entity_type,
description=description,
+ external_entity_id=external_entity_id,
additional_attributes=additional_attributes,
request_options=request_options,
)
@@ -686,6 +701,7 @@ async def update_entity(
*,
name: typing.Optional[str] = OMIT,
description: typing.Optional[str] = OMIT,
+ external_entity_id: typing.Optional[str] = OMIT,
additional_attributes: typing.Optional[AdditionalAttributes] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
) -> EntityResponse:
@@ -703,6 +719,9 @@ async def update_entity(
description : typing.Optional[str]
Updated description.
+ external_entity_id : typing.Optional[str]
+ Updated external identifier for this entity.
+
additional_attributes : typing.Optional[AdditionalAttributes]
request_options : typing.Optional[RequestOptions]
@@ -746,6 +765,7 @@ async def main() -> None:
entity_id,
name=name,
description=description,
+ external_entity_id=external_entity_id,
additional_attributes=additional_attributes,
request_options=request_options,
)
diff --git a/src/newscatcher_catchall/entities/raw_client.py b/src/newscatcher_catchall/entities/raw_client.py
index 64759f3..2607f89 100644
--- a/src/newscatcher_catchall/entities/raw_client.py
+++ b/src/newscatcher_catchall/entities/raw_client.py
@@ -127,6 +127,7 @@ def create_entity(
name: str,
entity_type: typing.Optional[EntityType] = OMIT,
description: typing.Optional[str] = OMIT,
+ external_entity_id: typing.Optional[str] = OMIT,
additional_attributes: typing.Optional[AdditionalAttributes] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
) -> HttpResponse[CreateEntityResponse]:
@@ -148,6 +149,9 @@ def create_entity(
description : typing.Optional[str]
Free-text description of the entity used for disambiguation when similar names exist.
+ external_entity_id : typing.Optional[str]
+ Optional external identifier for this entity. Free-form string, not enforced as unique. Use it to store your own CRM, data warehouse, or internal database ID so you can join CatchAll results back to your systems.
+
additional_attributes : typing.Optional[AdditionalAttributes]
request_options : typing.Optional[RequestOptions]
@@ -165,6 +169,7 @@ def create_entity(
"name": name,
"entity_type": entity_type,
"description": description,
+ "external_entity_id": external_entity_id,
"additional_attributes": convert_and_respect_annotation_metadata(
object_=additional_attributes, annotation=AdditionalAttributes, direction="write"
),
@@ -419,6 +424,7 @@ def update_entity(
*,
name: typing.Optional[str] = OMIT,
description: typing.Optional[str] = OMIT,
+ external_entity_id: typing.Optional[str] = OMIT,
additional_attributes: typing.Optional[AdditionalAttributes] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
) -> HttpResponse[EntityResponse]:
@@ -436,6 +442,9 @@ def update_entity(
description : typing.Optional[str]
Updated description.
+ external_entity_id : typing.Optional[str]
+ Updated external identifier for this entity.
+
additional_attributes : typing.Optional[AdditionalAttributes]
request_options : typing.Optional[RequestOptions]
@@ -452,6 +461,7 @@ def update_entity(
json={
"name": name,
"description": description,
+ "external_entity_id": external_entity_id,
"additional_attributes": convert_and_respect_annotation_metadata(
object_=additional_attributes, annotation=AdditionalAttributes, direction="write"
),
@@ -611,6 +621,7 @@ async def create_entity(
name: str,
entity_type: typing.Optional[EntityType] = OMIT,
description: typing.Optional[str] = OMIT,
+ external_entity_id: typing.Optional[str] = OMIT,
additional_attributes: typing.Optional[AdditionalAttributes] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
) -> AsyncHttpResponse[CreateEntityResponse]:
@@ -632,6 +643,9 @@ async def create_entity(
description : typing.Optional[str]
Free-text description of the entity used for disambiguation when similar names exist.
+ external_entity_id : typing.Optional[str]
+ Optional external identifier for this entity. Free-form string, not enforced as unique. Use it to store your own CRM, data warehouse, or internal database ID so you can join CatchAll results back to your systems.
+
additional_attributes : typing.Optional[AdditionalAttributes]
request_options : typing.Optional[RequestOptions]
@@ -649,6 +663,7 @@ async def create_entity(
"name": name,
"entity_type": entity_type,
"description": description,
+ "external_entity_id": external_entity_id,
"additional_attributes": convert_and_respect_annotation_metadata(
object_=additional_attributes, annotation=AdditionalAttributes, direction="write"
),
@@ -903,6 +918,7 @@ async def update_entity(
*,
name: typing.Optional[str] = OMIT,
description: typing.Optional[str] = OMIT,
+ external_entity_id: typing.Optional[str] = OMIT,
additional_attributes: typing.Optional[AdditionalAttributes] = OMIT,
request_options: typing.Optional[RequestOptions] = None,
) -> AsyncHttpResponse[EntityResponse]:
@@ -920,6 +936,9 @@ async def update_entity(
description : typing.Optional[str]
Updated description.
+ external_entity_id : typing.Optional[str]
+ Updated external identifier for this entity.
+
additional_attributes : typing.Optional[AdditionalAttributes]
request_options : typing.Optional[RequestOptions]
@@ -936,6 +955,7 @@ async def update_entity(
json={
"name": name,
"description": description,
+ "external_entity_id": external_entity_id,
"additional_attributes": convert_and_respect_annotation_metadata(
object_=additional_attributes, annotation=AdditionalAttributes, direction="write"
),
diff --git a/src/newscatcher_catchall/jobs/client.py b/src/newscatcher_catchall/jobs/client.py
index dbede84..e277aa9 100644
--- a/src/newscatcher_catchall/jobs/client.py
+++ b/src/newscatcher_catchall/jobs/client.py
@@ -394,6 +394,37 @@ def get_job_results(
)
return _response.data
+ def get_job_results_csv(self, job_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> str:
+ """
+ Returns a completed job's result records as a CSV download. One row per record, with enrichment fields as columns, citations as a JSON column, and connected entities split into `event_associated_entities` and `mention_entities` JSON columns.
+
+ Parameters
+ ----------
+ job_id : str
+ Unique job identifier returned from [`POST /catchAll/submit`](https://www.newscatcherapi.com/docs/web-search-api/api-reference/jobs/create-job).
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ str
+ CSV export of job results.
+
+ Examples
+ --------
+ from newscatcher_catchall import CatchAllApi
+
+ client = CatchAllApi(
+ api_key="YOUR_API_KEY",
+ )
+ client.jobs.get_job_results_csv(
+ job_id="job_id",
+ )
+ """
+ _response = self._raw_client.get_job_results_csv(job_id, request_options=request_options)
+ return _response.data
+
def continue_job(
self,
*,
@@ -886,6 +917,45 @@ async def main() -> None:
)
return _response.data
+ async def get_job_results_csv(self, job_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> str:
+ """
+ Returns a completed job's result records as a CSV download. One row per record, with enrichment fields as columns, citations as a JSON column, and connected entities split into `event_associated_entities` and `mention_entities` JSON columns.
+
+ Parameters
+ ----------
+ job_id : str
+ Unique job identifier returned from [`POST /catchAll/submit`](https://www.newscatcherapi.com/docs/web-search-api/api-reference/jobs/create-job).
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ str
+ CSV export of job results.
+
+ Examples
+ --------
+ import asyncio
+
+ from newscatcher_catchall import AsyncCatchAllApi
+
+ client = AsyncCatchAllApi(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.jobs.get_job_results_csv(
+ job_id="job_id",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.get_job_results_csv(job_id, request_options=request_options)
+ return _response.data
+
async def continue_job(
self,
*,
diff --git a/src/newscatcher_catchall/jobs/raw_client.py b/src/newscatcher_catchall/jobs/raw_client.py
index c1de127..86a8d58 100644
--- a/src/newscatcher_catchall/jobs/raw_client.py
+++ b/src/newscatcher_catchall/jobs/raw_client.py
@@ -598,6 +598,64 @@ def get_job_results(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+ def get_job_results_csv(
+ self, job_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[str]:
+ """
+ Returns a completed job's result records as a CSV download. One row per record, with enrichment fields as columns, citations as a JSON column, and connected entities split into `event_associated_entities` and `mention_entities` JSON columns.
+
+ Parameters
+ ----------
+ job_id : str
+ Unique job identifier returned from [`POST /catchAll/submit`](https://www.newscatcherapi.com/docs/web-search-api/api-reference/jobs/create-job).
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[str]
+ CSV export of job results.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"catchAll/pull/{encode_path_param(job_id)}/csv",
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return HttpResponse(response=_response, data=_response.text) # type: ignore
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ Error,
+ parse_obj_as(
+ type_=Error, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ Error,
+ parse_obj_as(
+ type_=Error, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
def continue_job(
self,
*,
@@ -1315,6 +1373,64 @@ async def get_job_results(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+ async def get_job_results_csv(
+ self, job_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[str]:
+ """
+ Returns a completed job's result records as a CSV download. One row per record, with enrichment fields as columns, citations as a JSON column, and connected entities split into `event_associated_entities` and `mention_entities` JSON columns.
+
+ Parameters
+ ----------
+ job_id : str
+ Unique job identifier returned from [`POST /catchAll/submit`](https://www.newscatcherapi.com/docs/web-search-api/api-reference/jobs/create-job).
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[str]
+ CSV export of job results.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"catchAll/pull/{encode_path_param(job_id)}/csv",
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return AsyncHttpResponse(response=_response, data=_response.text) # type: ignore
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ Error,
+ parse_obj_as(
+ type_=Error, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ Error,
+ parse_obj_as(
+ type_=Error, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
async def continue_job(
self,
*,
diff --git a/src/newscatcher_catchall/monitors/client.py b/src/newscatcher_catchall/monitors/client.py
index 37e5bdc..7d3fe89 100644
--- a/src/newscatcher_catchall/monitors/client.py
+++ b/src/newscatcher_catchall/monitors/client.py
@@ -210,6 +210,39 @@ def pull_monitor_results(
_response = self._raw_client.pull_monitor_results(monitor_id, request_options=request_options)
return _response.data
+ def pull_monitor_results_csv(
+ self, monitor_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> str:
+ """
+ Returns the most recent run's records as a CSV download. One row per record, with enrichment fields as columns, citations as a JSON column, and connected entities split into `event_associated_entities` and `mention_entities` JSON columns.
+
+ Parameters
+ ----------
+ monitor_id : str
+ Monitor identifier.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ str
+ CSV export of the latest monitor run.
+
+ Examples
+ --------
+ from newscatcher_catchall import CatchAllApi
+
+ client = CatchAllApi(
+ api_key="YOUR_API_KEY",
+ )
+ client.monitors.pull_monitor_results_csv(
+ monitor_id="monitor_id",
+ )
+ """
+ _response = self._raw_client.pull_monitor_results_csv(monitor_id, request_options=request_options)
+ return _response.data
+
def list_monitor_jobs(
self,
monitor_id: str,
@@ -662,6 +695,47 @@ async def main() -> None:
_response = await self._raw_client.pull_monitor_results(monitor_id, request_options=request_options)
return _response.data
+ async def pull_monitor_results_csv(
+ self, monitor_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> str:
+ """
+ Returns the most recent run's records as a CSV download. One row per record, with enrichment fields as columns, citations as a JSON column, and connected entities split into `event_associated_entities` and `mention_entities` JSON columns.
+
+ Parameters
+ ----------
+ monitor_id : str
+ Monitor identifier.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ str
+ CSV export of the latest monitor run.
+
+ Examples
+ --------
+ import asyncio
+
+ from newscatcher_catchall import AsyncCatchAllApi
+
+ client = AsyncCatchAllApi(
+ api_key="YOUR_API_KEY",
+ )
+
+
+ async def main() -> None:
+ await client.monitors.pull_monitor_results_csv(
+ monitor_id="monitor_id",
+ )
+
+
+ asyncio.run(main())
+ """
+ _response = await self._raw_client.pull_monitor_results_csv(monitor_id, request_options=request_options)
+ return _response.data
+
async def list_monitor_jobs(
self,
monitor_id: str,
diff --git a/src/newscatcher_catchall/monitors/raw_client.py b/src/newscatcher_catchall/monitors/raw_client.py
index 16f87f1..dc48387 100644
--- a/src/newscatcher_catchall/monitors/raw_client.py
+++ b/src/newscatcher_catchall/monitors/raw_client.py
@@ -295,6 +295,64 @@ def pull_monitor_results(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+ def pull_monitor_results_csv(
+ self, monitor_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> HttpResponse[str]:
+ """
+ Returns the most recent run's records as a CSV download. One row per record, with enrichment fields as columns, citations as a JSON column, and connected entities split into `event_associated_entities` and `mention_entities` JSON columns.
+
+ Parameters
+ ----------
+ monitor_id : str
+ Monitor identifier.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ HttpResponse[str]
+ CSV export of the latest monitor run.
+ """
+ _response = self._client_wrapper.httpx_client.request(
+ f"catchAll/monitors/pull/{encode_path_param(monitor_id)}/csv",
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return HttpResponse(response=_response, data=_response.text) # type: ignore
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ Error,
+ parse_obj_as(
+ type_=Error, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ Error,
+ parse_obj_as(
+ type_=Error, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
def list_monitor_jobs(
self,
monitor_id: str,
@@ -1035,6 +1093,64 @@ async def pull_monitor_results(
)
raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+ async def pull_monitor_results_csv(
+ self, monitor_id: str, *, request_options: typing.Optional[RequestOptions] = None
+ ) -> AsyncHttpResponse[str]:
+ """
+ Returns the most recent run's records as a CSV download. One row per record, with enrichment fields as columns, citations as a JSON column, and connected entities split into `event_associated_entities` and `mention_entities` JSON columns.
+
+ Parameters
+ ----------
+ monitor_id : str
+ Monitor identifier.
+
+ request_options : typing.Optional[RequestOptions]
+ Request-specific configuration.
+
+ Returns
+ -------
+ AsyncHttpResponse[str]
+ CSV export of the latest monitor run.
+ """
+ _response = await self._client_wrapper.httpx_client.request(
+ f"catchAll/monitors/pull/{encode_path_param(monitor_id)}/csv",
+ method="GET",
+ request_options=request_options,
+ )
+ try:
+ if 200 <= _response.status_code < 300:
+ return AsyncHttpResponse(response=_response, data=_response.text) # type: ignore
+ if _response.status_code == 403:
+ raise ForbiddenError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ Error,
+ parse_obj_as(
+ type_=Error, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ if _response.status_code == 404:
+ raise NotFoundError(
+ headers=dict(_response.headers),
+ body=typing.cast(
+ Error,
+ parse_obj_as(
+ type_=Error, # type: ignore
+ object_=_response.json(),
+ ),
+ ),
+ )
+ _response_json = _response.json()
+ except JSONDecodeError:
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
+ except ValidationError as e:
+ raise ParsingError(
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
+ )
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
+
async def list_monitor_jobs(
self,
monitor_id: str,
diff --git a/src/newscatcher_catchall/types/__init__.py b/src/newscatcher_catchall/types/__init__.py
index 018e931..76e0f9f 100644
--- a/src/newscatcher_catchall/types/__init__.py
+++ b/src/newscatcher_catchall/types/__init__.py
@@ -22,6 +22,7 @@
from .company_enrichment_output_metadata_domain_url_confidence import (
CompanyEnrichmentOutputMetadataDomainUrlConfidence,
)
+ from .connected_dataset import ConnectedDataset
from .connected_entity import ConnectedEntity
from .context import Context
from .continue_response_dto import ContinueResponseDto
@@ -144,6 +145,7 @@
"CompanyEnrichmentOutput": ".company_enrichment_output",
"CompanyEnrichmentOutputMetadata": ".company_enrichment_output_metadata",
"CompanyEnrichmentOutputMetadataDomainUrlConfidence": ".company_enrichment_output_metadata_domain_url_confidence",
+ "ConnectedDataset": ".connected_dataset",
"ConnectedEntity": ".connected_entity",
"Context": ".context",
"ContinueResponseDto": ".continue_response_dto",
@@ -290,6 +292,7 @@ def __dir__():
"CompanyEnrichmentOutput",
"CompanyEnrichmentOutputMetadata",
"CompanyEnrichmentOutputMetadataDomainUrlConfidence",
+ "ConnectedDataset",
"ConnectedEntity",
"Context",
"ContinueResponseDto",
diff --git a/src/newscatcher_catchall/types/connected_dataset.py b/src/newscatcher_catchall/types/connected_dataset.py
new file mode 100644
index 0000000..82f0ba5
--- /dev/null
+++ b/src/newscatcher_catchall/types/connected_dataset.py
@@ -0,0 +1,36 @@
+# This file was auto-generated by Fern from our API Definition.
+
+import typing
+
+import pydantic
+from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+
+
+class ConnectedDataset(UniversalBaseModel):
+ """
+ A dataset used to narrow the retrieval scope of a job or monitor, returned with its identifier and name.
+ """
+
+ id: str = pydantic.Field()
+ """
+ Dataset identifier.
+ """
+
+ name: str = pydantic.Field()
+ """
+ Dataset name as stored. Check `is_deleted` to determine whether the dataset is still active.
+ """
+
+ is_deleted: typing.Optional[bool] = pydantic.Field(default=None)
+ """
+ True when the dataset has been deleted.
+ """
+
+ if IS_PYDANTIC_V2:
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
+ else:
+
+ class Config:
+ frozen = True
+ smart_union = True
+ extra = pydantic.Extra.allow
diff --git a/src/newscatcher_catchall/types/connected_entity.py b/src/newscatcher_catchall/types/connected_entity.py
index 82adc82..f5f68f4 100644
--- a/src/newscatcher_catchall/types/connected_entity.py
+++ b/src/newscatcher_catchall/types/connected_entity.py
@@ -51,9 +51,14 @@ class ConnectedEntity(UniversalBaseModel):
The entity type.
"""
+ external_entity_id: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ External identifier for this entity. Null when not set.
+ """
+
company: typing.Optional[CompanyAttributes] = pydantic.Field(default=None)
"""
- The stored attributes for this entity. Present only when attributes exist in the database.
+ The stored attributes for this entity. Present only when attributes exist in the database.
The field name matches the value of `type` — for example, `"company"` type entities have a `company` field.
"""
diff --git a/src/newscatcher_catchall/types/create_entity_request.py b/src/newscatcher_catchall/types/create_entity_request.py
index 8ef4123..3539791 100644
--- a/src/newscatcher_catchall/types/create_entity_request.py
+++ b/src/newscatcher_catchall/types/create_entity_request.py
@@ -25,6 +25,11 @@ class CreateEntityRequest(UniversalBaseModel):
Free-text description of the entity used for disambiguation when similar names exist.
"""
+ external_entity_id: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ Optional external identifier for this entity. Free-form string, not enforced as unique. Use it to store your own CRM, data warehouse, or internal database ID so you can join CatchAll results back to your systems.
+ """
+
additional_attributes: typing.Optional[AdditionalAttributes] = None
if IS_PYDANTIC_V2:
diff --git a/src/newscatcher_catchall/types/entity_response.py b/src/newscatcher_catchall/types/entity_response.py
index 3b100d6..ebde3eb 100644
--- a/src/newscatcher_catchall/types/entity_response.py
+++ b/src/newscatcher_catchall/types/entity_response.py
@@ -32,6 +32,11 @@ class EntityResponse(UniversalBaseModel):
Free-text description.
"""
+ external_entity_id: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ External identifier for this entity. Null when not set.
+ """
+
additional_attributes: typing.Optional[AdditionalAttributes] = None
status: EntityStatus
created_by_user_id: typing.Optional[str] = pydantic.Field(default=None)
diff --git a/src/newscatcher_catchall/types/entity_summary.py b/src/newscatcher_catchall/types/entity_summary.py
index 50fdc5b..465b763 100644
--- a/src/newscatcher_catchall/types/entity_summary.py
+++ b/src/newscatcher_catchall/types/entity_summary.py
@@ -34,6 +34,11 @@ class EntitySummary(UniversalBaseModel):
Free-text description.
"""
+ external_entity_id: typing.Optional[str] = pydantic.Field(default=None)
+ """
+ External identifier for this entity. Null when not set.
+ """
+
attributes: typing.Optional[CompanyAttributes] = None
if IS_PYDANTIC_V2:
diff --git a/src/newscatcher_catchall/types/pull_job_response_dto.py b/src/newscatcher_catchall/types/pull_job_response_dto.py
index 187087c..b9da2e5 100644
--- a/src/newscatcher_catchall/types/pull_job_response_dto.py
+++ b/src/newscatcher_catchall/types/pull_job_response_dto.py
@@ -4,6 +4,7 @@
import pydantic
from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .connected_dataset import ConnectedDataset
from .pull_job_response_dto_date_range import PullJobResponseDtoDateRange
from .pull_job_response_dto_mode import PullJobResponseDtoMode
from .record import Record
@@ -94,6 +95,16 @@ class PullJobResponseDto(UniversalBaseModel):
Processing mode used for this job.
"""
+ connected_datasets: typing.Optional[typing.List[ConnectedDataset]] = pydantic.Field(default=None)
+ """
+ Datasets used to narrow retrieval scope, each with `id` and `name`.
+ """
+
+ is_all_news_query: typing.Optional[bool] = pydantic.Field(default=None)
+ """
+ True when the query was submitted as an all-news (watchlist-generic) query.
+ """
+
sharing_info: typing.Optional[SharingInfo] = pydantic.Field(default=None)
"""
Present when the job was shared with the authenticated user by another organization member. `null` when the user owns the job.
diff --git a/src/newscatcher_catchall/types/pull_monitor_response_dto.py b/src/newscatcher_catchall/types/pull_monitor_response_dto.py
index ce08a6b..12d58a7 100644
--- a/src/newscatcher_catchall/types/pull_monitor_response_dto.py
+++ b/src/newscatcher_catchall/types/pull_monitor_response_dto.py
@@ -4,6 +4,7 @@
import pydantic
from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
+from .connected_dataset import ConnectedDataset
from .monitor_record import MonitorRecord
from .pull_monitor_response_dto_run_info import PullMonitorResponseDtoRunInfo
from .reference_job import ReferenceJob
@@ -53,6 +54,16 @@ class PullMonitorResponseDto(UniversalBaseModel):
Record limit applied to this monitor's jobs.
"""
+ connected_datasets: typing.Optional[typing.List[ConnectedDataset]] = pydantic.Field(default=None)
+ """
+ Datasets used to narrow retrieval scope, each with `id` and `name`.
+ """
+
+ is_all_news_query: typing.Optional[bool] = pydantic.Field(default=None)
+ """
+ True when the reference job was submitted as an all-news (watchlist-generic) query.
+ """
+
if IS_PYDANTIC_V2:
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
else: