From d81ac8871a6477398343cade375c81834ed4913a Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 08:56:57 +0000 Subject: [PATCH] feat: remove deprecated searchsimilar client module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The searchsimilar client module and all its associated functionality have been removed from the SDK. This affects both synchronous and asynchronous clients that provided methods for searching similar articles. This removal is part of API consolidation efforts to streamline the SDK interface and reduce maintenance overhead. Key changes: - Removed SearchsimilarClient and AsyncSearchsimilarClient classes - Removed searchsimilar.get() and searchsimilar.post() methods - Removed all searchsimilar-related type definitions and imports - Removed raw client implementations for searchsimilar endpoints 🌿 Generated with Fern --- .fern/metadata.json | 15 + .fernignore | 3 +- changelog.md | 18 + poetry.lock | 143 +- pyproject.toml | 9 +- reference.md | 2991 ++++------------- src/newscatcher/__init__.py | 487 +-- src/newscatcher/aggregation/__init__.py | 46 +- src/newscatcher/aggregation/client.py | 751 ++--- src/newscatcher/aggregation/raw_client.py | 689 ++-- src/newscatcher/aggregation/types/__init__.py | 45 +- ...e.py => aggregation_count_get_response.py} | 2 +- ....py => aggregation_count_post_response.py} | 2 +- ...on_get_request_published_date_precision.py | 7 - .../types/aggregation_get_request_sort_by.py | 5 - src/newscatcher/authors/__init__.py | 43 +- src/newscatcher/authors/client.py | 633 ++-- src/newscatcher/authors/raw_client.py | 573 +--- src/newscatcher/authors/types/__init__.py | 45 +- ...rs_get_request_published_date_precision.py | 5 - .../types/authors_get_request_sort_by.py | 5 - src/newscatcher/base_client.py | 194 +- src/newscatcher/breaking_news/__init__.py | 3 - src/newscatcher/breaking_news/client.py | 394 +-- src/newscatcher/breaking_news/raw_client.py | 381 +-- .../breaking_news/types/__init__.py | 7 - .../breaking_news_get_request_sort_by.py | 5 - src/newscatcher/core/__init__.py | 113 +- src/newscatcher/core/client_wrapper.py | 29 +- src/newscatcher/core/datetime_utils.py | 42 + src/newscatcher/core/force_multipart.py | 6 +- src/newscatcher/core/http_client.py | 489 ++- src/newscatcher/core/http_response.py | 8 +- src/newscatcher/core/http_sse/__init__.py | 42 + src/newscatcher/core/http_sse/_api.py | 112 + src/newscatcher/core/http_sse/_decoders.py | 61 + .../http_sse/_exceptions.py} | 6 +- src/newscatcher/core/http_sse/_models.py | 17 + src/newscatcher/core/jsonable_encoder.py | 8 + src/newscatcher/core/logging.py | 107 + src/newscatcher/core/parse_error.py | 36 + src/newscatcher/core/pydantic_utilities.py | 411 ++- src/newscatcher/errors/__init__.py | 49 +- src/newscatcher/latest_headlines/__init__.py | 37 + .../client.py | 711 ++-- .../raw_client.py | 623 +--- .../latest_headlines/types/__init__.py | 38 + .../types/latest_headlines_get_response.py | 0 .../types/latest_headlines_post_response.py | 0 src/newscatcher/latestheadlines/__init__.py | 7 - .../latestheadlines/types/__init__.py | 9 - ...adlines_get_request_clustering_variable.py | 5 - src/newscatcher/search/__init__.py | 47 +- src/newscatcher/search/client.py | 909 ++--- src/newscatcher/search/raw_client.py | 821 ++--- src/newscatcher/search/types/__init__.py | 49 +- .../search_get_request_clustering_variable.py | 5 - .../search_get_request_news_domain_type.py | 7 - ...ch_get_request_published_date_precision.py | 5 - .../types/search_get_request_sort_by.py | 5 - .../__init__.py | 0 .../{search_link => search_by_link}/client.py | 112 +- .../raw_client.py | 72 +- src/newscatcher/searchsimilar/__init__.py | 17 - src/newscatcher/searchsimilar/client.py | 1360 -------- src/newscatcher/searchsimilar/raw_client.py | 1684 ---------- .../searchsimilar/types/__init__.py | 15 - ...ar_get_request_published_date_precision.py | 7 - .../search_similar_get_request_sort_by.py | 5 - .../types/search_similar_get_response.py | 8 - .../types/search_similar_post_response.py | 8 - src/newscatcher/sources/__init__.py | 3 - src/newscatcher/sources/client.py | 157 +- src/newscatcher/sources/raw_client.py | 143 +- src/newscatcher/sources/types/__init__.py | 7 - .../sources_get_request_news_domain_type.py | 7 - src/newscatcher/subscription/raw_client.py | 18 + src/newscatcher/types/__init__.py | 359 +- .../types/additional_source_info.py | 18 +- .../types/aggregation_count_response_dto.py | 4 +- ...gation_count_response_dto_aggregations.py} | 2 +- ...lar_document.py => all_links_data_item.py} | 21 +- .../types/{authors.py => all_links_text.py} | 2 +- src/newscatcher/types/article_entity.py | 18 +- ...urnalists.py => article_entity_authors.py} | 2 +- .../types/article_entity_journalists.py | 5 + .../types/{articles.py => articles_dto.py} | 2 +- .../types/breaking_news_article_entity.py | 27 +- .../breaking_news_article_entity_authors.py | 5 + ...reaking_news_article_entity_journalists.py | 5 + .../types/failed_authors_response_dto.py | 2 +- .../failed_search_similar_response_dto.py | 30 - .../types/include_similar_documents.py | 3 - src/newscatcher/types/nlp_data_entity.py | 117 +- src/newscatcher/types/not_theme.py | 4 +- .../types/published_date_precision.py | 4 +- src/newscatcher/types/search_response_dto.py | 4 +- .../types/search_similar_response_dto.py | 35 - .../types/similar_article_entity.py | 30 - .../types/similar_documents_number.py | 3 - src/newscatcher/types/sources_response_dto.py | 2 +- src/newscatcher/types/theme.py | 4 +- src/newscatcher/types/user_input_dto.py | 2 +- tests/utils/test_http_client.py | 249 +- tests/utils/test_query_encoding.py | 1 - 105 files changed, 5670 insertions(+), 11228 deletions(-) create mode 100644 .fern/metadata.json create mode 100644 changelog.md rename src/newscatcher/aggregation/types/{aggregation_get_response.py => aggregation_count_get_response.py} (70%) rename src/newscatcher/aggregation/types/{aggregation_post_response.py => aggregation_count_post_response.py} (70%) delete mode 100644 src/newscatcher/aggregation/types/aggregation_get_request_published_date_precision.py delete mode 100644 src/newscatcher/aggregation/types/aggregation_get_request_sort_by.py delete mode 100644 src/newscatcher/authors/types/authors_get_request_published_date_precision.py delete mode 100644 src/newscatcher/authors/types/authors_get_request_sort_by.py delete mode 100644 src/newscatcher/breaking_news/types/__init__.py delete mode 100644 src/newscatcher/breaking_news/types/breaking_news_get_request_sort_by.py create mode 100644 src/newscatcher/core/http_sse/__init__.py create mode 100644 src/newscatcher/core/http_sse/_api.py create mode 100644 src/newscatcher/core/http_sse/_decoders.py rename src/newscatcher/{types/similar_documents_fields.py => core/http_sse/_exceptions.py} (51%) create mode 100644 src/newscatcher/core/http_sse/_models.py create mode 100644 src/newscatcher/core/logging.py create mode 100644 src/newscatcher/core/parse_error.py create mode 100644 src/newscatcher/latest_headlines/__init__.py rename src/newscatcher/{latestheadlines => latest_headlines}/client.py (56%) rename src/newscatcher/{latestheadlines => latest_headlines}/raw_client.py (67%) create mode 100644 src/newscatcher/latest_headlines/types/__init__.py rename src/newscatcher/{latestheadlines => latest_headlines}/types/latest_headlines_get_response.py (100%) rename src/newscatcher/{latestheadlines => latest_headlines}/types/latest_headlines_post_response.py (100%) delete mode 100644 src/newscatcher/latestheadlines/__init__.py delete mode 100644 src/newscatcher/latestheadlines/types/__init__.py delete mode 100644 src/newscatcher/latestheadlines/types/latest_headlines_get_request_clustering_variable.py delete mode 100644 src/newscatcher/search/types/search_get_request_clustering_variable.py delete mode 100644 src/newscatcher/search/types/search_get_request_news_domain_type.py delete mode 100644 src/newscatcher/search/types/search_get_request_published_date_precision.py delete mode 100644 src/newscatcher/search/types/search_get_request_sort_by.py rename src/newscatcher/{search_link => search_by_link}/__init__.py (100%) rename src/newscatcher/{search_link => search_by_link}/client.py (73%) rename src/newscatcher/{search_link => search_by_link}/raw_client.py (92%) delete mode 100644 src/newscatcher/searchsimilar/__init__.py delete mode 100644 src/newscatcher/searchsimilar/client.py delete mode 100644 src/newscatcher/searchsimilar/raw_client.py delete mode 100644 src/newscatcher/searchsimilar/types/__init__.py delete mode 100644 src/newscatcher/searchsimilar/types/search_similar_get_request_published_date_precision.py delete mode 100644 src/newscatcher/searchsimilar/types/search_similar_get_request_sort_by.py delete mode 100644 src/newscatcher/searchsimilar/types/search_similar_get_response.py delete mode 100644 src/newscatcher/searchsimilar/types/search_similar_post_response.py delete mode 100644 src/newscatcher/sources/types/__init__.py delete mode 100644 src/newscatcher/sources/types/sources_get_request_news_domain_type.py rename src/newscatcher/types/{aggregations.py => aggregation_count_response_dto_aggregations.py} (55%) rename src/newscatcher/types/{similar_document.py => all_links_data_item.py} (57%) rename src/newscatcher/types/{authors.py => all_links_text.py} (61%) rename src/newscatcher/types/{journalists.py => article_entity_authors.py} (57%) create mode 100644 src/newscatcher/types/article_entity_journalists.py rename src/newscatcher/types/{articles.py => articles_dto.py} (75%) create mode 100644 src/newscatcher/types/breaking_news_article_entity_authors.py create mode 100644 src/newscatcher/types/breaking_news_article_entity_journalists.py delete mode 100644 src/newscatcher/types/failed_search_similar_response_dto.py delete mode 100644 src/newscatcher/types/include_similar_documents.py delete mode 100644 src/newscatcher/types/search_similar_response_dto.py delete mode 100644 src/newscatcher/types/similar_article_entity.py delete mode 100644 src/newscatcher/types/similar_documents_number.py diff --git a/.fern/metadata.json b/.fern/metadata.json new file mode 100644 index 0000000..eebc315 --- /dev/null +++ b/.fern/metadata.json @@ -0,0 +1,15 @@ +{ + "cliVersion": "4.29.0", + "generatorName": "fernapi/fern-python-sdk", + "generatorVersion": "4.64.0", + "generatorConfig": { + "client": { + "class_name": "BaseNewscatcherApi", + "filename": "base_client.py", + "exported_class_name": "NewscatcherApi", + "exported_filename": "client.py" + } + }, + "originGitCommit": "ea3775d5199c3ed3efc0866eb726d2ed2141aeba", + "sdkVersion": "2.0.0" +} \ No newline at end of file diff --git a/.fernignore b/.fernignore index d75d43d..b31d824 100644 --- a/.fernignore +++ b/.fernignore @@ -15,4 +15,5 @@ test-mock-mode.sh mypy.ini # CI/CD -.github/workflows/ci.yml \ No newline at end of file +.github/workflows/ci.yml +changelog.md diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..eddc689 --- /dev/null +++ b/changelog.md @@ -0,0 +1,18 @@ +## 2.0.0 - 2026-03-16 +* The `searchsimilar` client module has been removed from the SDK. All methods including `searchsimilar.get()` and `searchsimilar.post()` are no longer available. Applications using these methods will need to migrate to alternative search functionality or use the core search endpoints directly. +* The RawSearchsimilarClient and AsyncRawSearchsimilarClient classes have been removed from the SDK. If your code directly imports or uses these raw client classes, switch to the standard SearchsimilarClient which provides the same functionality with a cleaner interface. +* The Search Similar functionality has been removed from the SDK, including all related types and client methods. This feature was deprecated and no longer supported by the API. Code using SearchSimilar endpoints or related types will need to be updated to use alternative search methods. +* The aggregation client methods have been renamed for clarity: `get()` is now `count_get()` and `post()` is now `count_post()`. These methods continue to function identically but with more descriptive names. Update your code to use the new method names, as the old names have been removed. +* The aggregation client methods have been renamed for clarity. The `get()` method is now `count_get()` and the `post()` method is now `count_post()`. Update your code to use the new method names - all parameters and return types remain the same. +* The SDK now supports lazy loading to improve startup performance and reduce memory usage. Clients are initialized only when accessed, reducing initial import time. Added optional logging configuration to base client constructors for better debugging capabilities. +* The HTTP client now features enhanced retry logic with intelligent rate-limit handling, comprehensive request/response logging capabilities, and improved URL building for tenant-based endpoints. A new status_code property is available on HTTP response objects. +* The SDK now includes enhanced Server-Sent Events parsing capabilities and improved Pydantic model field handling. These improvements provide better support for streaming APIs and more flexible input handling for model fields. +* The latest headlines client has been renamed and restructured. The module path has changed from `latestheadlines` to `latest_headlines`, and the client class is now called `LatestHeadlinesClient` instead of `LatestheadlinesClient`. Method names have changed from `get()` and `post()` to `latest_headlines_get()` and `latest_headlines_post()`. Update your imports from `client.latestheadlines` to `client.latest_headlines` and update method calls accordingly. +* The latest headlines client has been restructured with breaking changes to class and method names. The `RawLatestheadlinesClient` class is now `RawLatestHeadlinesClient`, and methods `get()` and `post()` are now `latest_headlines_get()` and `latest_headlines_post()` respectively. Additionally, many parameters now use strongly-typed classes instead of primitive types. Update your imports and method calls accordingly. +* The search_link module has been renamed to search_by_link. Update imports from `client.search_link` to `client.search_by_link`. Method names have also changed: `search_url_get()` is now `search_by_link_get()` and `search_url_post()` is now `search_by_link_post()`. Parameter types are now more strictly typed (Page, PageSize, RobotsCompliant) instead of primitive int/bool types. +* Added support for the new `all_links_text` parameter in Authors API methods, allowing searches based on link anchor text content. Parameter types have been updated to use proper type aliases for improved type safety. +* The breaking news API methods now use strongly-typed parameter classes instead of primitive types for improved type safety. The `robots_compliant` parameter has been removed from breaking news methods. +* The search API now includes a new `all_links_text` parameter for filtering articles by link anchor text. All search parameters have been enhanced with proper type definitions for improved IDE support and validation. +* The Search API now supports searching by link anchor text content with the new `all_links_text` parameter. When used, the response includes detailed link information in the `all_links_data` field. +* The SDK now includes enhanced type definitions for better API compatibility, with new translation fields available on breaking news articles when the `include_translation_fields` parameter is enabled. Server-Sent Events (SSE) support has been added for real-time data streaming capabilities. + diff --git a/poetry.lock b/poetry.lock index c2ddd86..83fdc8b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -38,13 +38,13 @@ trio = ["trio (>=0.26.1)"] [[package]] name = "certifi" -version = "2025.8.3" +version = "2026.2.25" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" files = [ - {file = "certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"}, - {file = "certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407"}, + {file = "certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa"}, + {file = "certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7"}, ] [[package]] @@ -60,13 +60,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.3.0" +version = "1.3.1" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, - {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, ] [package.dependencies] @@ -75,6 +75,20 @@ typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "execnet" +version = "2.1.2" +description = "execnet: rapid multi-Python deployment" +optional = false +python-versions = ">=3.8" +files = [ + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, +] + +[package.extras] +testing = ["hatch", "pre-commit", "pytest", "tox"] + [[package]] name = "h11" version = "0.16.0" @@ -133,13 +147,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "idna" -version = "3.10" +version = "3.11" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, ] [package.extras] @@ -222,13 +236,13 @@ files = [ [[package]] name = "packaging" -version = "25.0" +version = "26.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, + {file = "packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529"}, + {file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"}, ] [[package]] @@ -418,6 +432,26 @@ pytest = ">=7.0.0,<9" docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] +[[package]] +name = "pytest-xdist" +version = "3.6.1" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, + {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, +] + +[package.dependencies] +execnet = ">=2.1" +pytest = ">=7.0.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -483,43 +517,58 @@ files = [ [[package]] name = "tomli" -version = "2.2.1" +version = "2.4.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, - {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, - {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, - {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, - {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, - {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, - {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, - {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, - {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, - {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, + {file = "tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867"}, + {file = "tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9"}, + {file = "tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95"}, + {file = "tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76"}, + {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d"}, + {file = "tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576"}, + {file = "tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a"}, + {file = "tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa"}, + {file = "tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614"}, + {file = "tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1"}, + {file = "tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8"}, + {file = "tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a"}, + {file = "tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1"}, + {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b"}, + {file = "tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51"}, + {file = "tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729"}, + {file = "tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da"}, + {file = "tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3"}, + {file = "tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0"}, + {file = "tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e"}, + {file = "tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4"}, + {file = "tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e"}, + {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c"}, + {file = "tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f"}, + {file = "tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86"}, + {file = "tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87"}, + {file = "tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132"}, + {file = "tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6"}, + {file = "tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc"}, + {file = "tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66"}, + {file = "tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d"}, + {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702"}, + {file = "tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8"}, + {file = "tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776"}, + {file = "tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475"}, + {file = "tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2"}, + {file = "tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9"}, + {file = "tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0"}, + {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df"}, + {file = "tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d"}, + {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f"}, + {file = "tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b"}, + {file = "tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087"}, + {file = "tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd"}, + {file = "tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4"}, + {file = "tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a"}, + {file = "tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c"}, ] [[package]] @@ -547,4 +596,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "8551b871abee465e23fb0966d51f2c155fd257b55bdcb0c02d095de19f92f358" +content-hash = "bcf31a142c86d9e556553c8c260a93b563ac64a043076dbd48b26111d422c26e" diff --git a/pyproject.toml b/pyproject.toml index ba72212..8cfa4f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,10 @@ [project] name = "newscatcher-sdk" +dynamic = ["version"] [tool.poetry] name = "newscatcher-sdk" -version = "1.4.0" +version = "2.0.0" description = "" readme = "README.md" authors = [] @@ -18,6 +19,9 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: MacOS", @@ -31,7 +35,7 @@ packages = [ { include = "newscatcher", from = "src"} ] -[project.urls] +[tool.poetry.urls] Repository = 'https://github.com/Newscatcher/newscatcher-python' [tool.poetry.dependencies] @@ -45,6 +49,7 @@ typing_extensions = ">= 4.0.0" mypy = "==1.13.0" pytest = "^7.4.0" pytest-asyncio = "^0.23.5" +pytest-xdist = "^3.6.1" python-dateutil = "^2.9.0" types-python-dateutil = "^2.9.0.20240316" ruff = "==0.11.5" diff --git a/reference.md b/reference.md index 367099b..1d8fdb2 100644 --- a/reference.md +++ b/reference.md @@ -1,6 +1,6 @@ # Reference ## Search -
client.search.get(...) +
client.search.get(...) -> SearchGetResponse
@@ -12,7 +12,7 @@
-Searches for articles based on specified criteria such as keyword, language, country, source, and more. +Searches for articles based on specified criteria such as keywords, language, country, source, and more.
@@ -27,33 +27,70 @@ Searches for articles based on specified criteria such as keyword, language, cou
```python -import datetime - from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment +import datetime client = NewscatcherApi( - api_key="YOUR_API_KEY", + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) + client.search.get( - q="technology AND (Apple OR Microsoft) NOT Google", + q="\"supply chain\" AND Amazon NOT China", search_in="title_content, title_content_translated", include_translation_fields=True, - predefined_sources="top 100 US, top 5 GB", - from_=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", - ), + predefined_sources="top 50 US, top 20 GB", + source_name="sport,tech", + sources="nytimes.com,finance.yahoo.com", + not_sources="cnn.com,wsj.com", + lang="en,es", + not_lang="fr,de", + countries="US,CA", + not_countries="UK,FR", + not_author_name="John Doe, Jane Doe", + from_=datetime.datetime.fromisoformat("2024-07-01T00:00:00+00:00"), + to=datetime.datetime.fromisoformat("2024-01-01T00:00:00+00:00"), + published_date_precision="full", + by_parse_date=True, + ranked_only=True, + from_rank=100, + to_rank=100, + is_headline=True, + is_opinion=True, + is_paid_content=False, + parent_url="wsj.com/politics,wsj.com/tech", + all_links="https://aiindex.stanford.edu/report,https://www.stateof.ai", + all_domain_links="who.int,nih.gov", + all_links_text="Nvidia,Tesla", + additional_domain_info=True, + is_news_domain=True, + news_type="General News Outlets,Tech News and Updates", + word_count_min=300, + word_count_max=1000, + page=2, + page_size=50, + clustering_enabled=True, + clustering_threshold=0.6, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", + theme="Finance,Tech", + not_theme="Crime,Sports", + org_entity_name="\"Apple Inc\" OR Microsoft", + per_entity_name="\"Elon Musk\" OR \"Jeff Bezos\"", + loc_entity_name="\"San Francisco\" OR \"New York City\"", + misc_entity_name="AWS OR \"Microsoft Azure\"", + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, iptc_tags="20000199,20000209", not_iptc_tags="20000205,20000209", iab_tags="Business,Events", not_iab_tags="Agriculture,Metals", - custom_tags="Tag1,Tag2,Tag3", + custom_tags="Tag1,Tag2", + exclude_duplicates=True, + robots_compliant=True, ) ``` @@ -70,18 +107,7 @@ client.search.get(
-**q:** `str` - -The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - -- For an exact match, use double quotes. For example, `"technology news"`. -- Use `*` to search for any keyword. -- Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. -- Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - -For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). +**q:** `Q`
@@ -109,13 +135,9 @@ For more details, see [Advanced querying](/docs/v3/documentation/guides-and-conc Predefined top news sources per country. -Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. +Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). -Examples: -- `"top 100 US"` -- `"top 33 AT"` -- `"top 50 US, top 20 GB"` -- `"top 33 AT, top 50 IT"` +Multiple countries with the number of top sources can be specified as a comma-separated string.
@@ -127,8 +149,6 @@ Examples: Word or phrase to search within the source names. To specify multiple values, use a comma-separated string. -Example: `"sport, tech"` - **Note**: The search doesn't require an exact match and returns sources containing the specified terms in their names. You can use any word or phrase, like `"sport"` or `"new york times"`. For example, `"sport"` returns sources such as `"Motorsport"`, `"Dot Esport"`, and `"Tuttosport"`. @@ -137,13 +157,7 @@ Example: `"sport, tech"`
-**sources:** `typing.Optional[str]` - -One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - -Examples: -- `"nytimes.com"` -- `"theguardian.com, finance.yahoo.com"` +**sources:** `typing.Optional[str]` — One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string.
@@ -151,11 +165,7 @@ Examples:
-**not_sources:** `typing.Optional[str]` - -The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - -Example: `"cnn.com, wsj.com"` +**not_sources:** `typing.Optional[str]` — The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string.
@@ -165,11 +175,9 @@ Example: `"cnn.com, wsj.com"` **lang:** `typing.Optional[str]` -The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - -Example: `"en, es"` +The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. -To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). +To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). @@ -181,9 +189,7 @@ To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/ove The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. -Example: `"fr, de"` - -To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). +To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). @@ -195,9 +201,7 @@ To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/ove The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. -Example: `"US, CA"` - -To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). +To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). @@ -209,9 +213,7 @@ To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/over The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. -Example:`"US, CA"` - -To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). +To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). @@ -219,11 +221,7 @@ To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/over
-**not_author_name:** `typing.Optional[str]` - -The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - -Example: `"John Doe, Jane Doe"` +**not_author_name:** `typing.Optional[str]` — The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string.
@@ -231,18 +229,7 @@ Example: `"John Doe, Jane Doe"`
-**from_:** `typing.Optional[dt.datetime]` - -The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - -Formats with examples: -- YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` -- YYYY-MM-dd: `2024-07-01` -- YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` -- YYYY/mm/dd: `2024/07/01` -- English phrases: `7 day ago`, `today` - -**Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. +**from:** `typing.Optional[From]`
@@ -250,18 +237,7 @@ Formats with examples:
-**to:** `typing.Optional[dt.datetime]` - -The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - -Formats with examples: -- YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` -- YYYY-MM-dd: `2024-07-01` -- YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` -- YYYY/mm/dd: `2024/07/01` -- English phrases: `1 day ago`, `now` - -**Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. +**to:** `typing.Optional[To]`
@@ -269,12 +245,7 @@ Formats with examples:
-**published_date_precision:** `typing.Optional[SearchGetRequestPublishedDatePrecision]` - -The precision of the published date. There are three types: -- `full`: The day and time of an article is correctly identified with the appropriate timezone. -- `timezone unknown`: The day and time of an article is correctly identified without timezone. -- `date`: Only the day is identified without an exact time. +**published_date_precision:** `typing.Optional[PublishedDatePrecision]`
@@ -282,7 +253,7 @@ The precision of the published date. There are three types:
-**by_parse_date:** `typing.Optional[bool]` — If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. +**by_parse_date:** `typing.Optional[ByParseDate]`
@@ -290,12 +261,7 @@ The precision of the published date. There are three types:
-**sort_by:** `typing.Optional[SearchGetRequestSortBy]` - -The sorting order of the results. Possible values are: -- `relevancy`: The most relevant results first. -- `date`: The most recently published results first. -- `rank`: The results from the highest-ranked sources first. +**sort_by:** `typing.Optional[SortBy]`
@@ -303,7 +269,7 @@ The sorting order of the results. Possible values are:
-**ranked_only:** `typing.Optional[bool]` — If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. +**ranked_only:** `typing.Optional[RankedOnly]`
@@ -311,7 +277,7 @@ The sorting order of the results. Possible values are:
-**from_rank:** `typing.Optional[int]` — The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. +**from_rank:** `typing.Optional[FromRank]`
@@ -319,7 +285,7 @@ The sorting order of the results. Possible values are:
-**to_rank:** `typing.Optional[int]` — The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. +**to_rank:** `typing.Optional[ToRank]`
@@ -327,7 +293,7 @@ The sorting order of the results. Possible values are:
-**is_headline:** `typing.Optional[bool]` — If true, only returns articles that were posted on the home page of a given news domain. +**is_headline:** `typing.Optional[IsHeadline]`
@@ -335,7 +301,7 @@ The sorting order of the results. Possible values are:
-**is_opinion:** `typing.Optional[bool]` — If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. +**is_opinion:** `typing.Optional[IsOpinion]`
@@ -343,7 +309,7 @@ The sorting order of the results. Possible values are:
-**is_paid_content:** `typing.Optional[bool]` — If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. +**is_paid_content:** `typing.Optional[IsPaidContent]`
@@ -351,11 +317,7 @@ The sorting order of the results. Possible values are:
-**parent_url:** `typing.Optional[str]` - -The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - -Example: `"wsj.com/politics, wsj.com/tech"` +**parent_url:** `typing.Optional[str]` — The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string.
@@ -367,9 +329,7 @@ Example: `"wsj.com/politics, wsj.com/tech"` The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. -Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - -For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). +For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). @@ -381,9 +341,7 @@ For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-ur The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. -Example: `"who.int, nih.gov"` - -For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). +For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). @@ -391,12 +349,13 @@ For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-ur
-**additional_domain_info:** `typing.Optional[bool]` +**all_links_text:** `typing.Optional[str]` + +The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. + +**Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. -If true, includes additional domain information in the response for each article: -- `is_news_domain`: Boolean indicating if the source is a news domain. -- `news_domain_type`: Type of news domain (e.g., `"Original Content"`). -- `news_type`: Category of news (e.g., `"News and Blogs"`). +To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url).
@@ -404,7 +363,7 @@ If true, includes additional domain information in the response for each article
-**is_news_domain:** `typing.Optional[bool]` — If true, filters results to include only news domains. +**additional_domain_info:** `typing.Optional[AdditionalDomainInfo]`
@@ -412,14 +371,15 @@ If true, includes additional domain information in the response for each article
-**news_domain_type:** `typing.Optional[SearchGetRequestNewsDomainType]` +**is_news_domain:** `typing.Optional[IsNewsDomain]` + +
+
+ +
+
-Filters results based on the news domain type. Possible values are: -- `Original Content`: Sources that produce their own content. -- `Aggregator`: Sources that collect content from various other sources. -- `Press Releases`: Sources primarily publishing press releases. -- `Republisher`: Sources that republish content from other sources. -- `Other`: Sources that don't fit into main categories. +**news_domain_type:** `typing.Optional[NewsDomainType]`
@@ -431,9 +391,7 @@ Filters results based on the news domain type. Possible values are: Filters results based on the news type. Multiple types can be specified using a comma-separated string. -Example: `"General News Outlets,Tech News and Updates"` - -For a complete list of available news types, see [Enumerated parameters > News type](/docs/v3/api-reference/overview/enumerated-parameters#news-type-news-type). +For a complete list of available news types, see [Enumerated parameters > News type](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#news-type-news-type). @@ -441,7 +399,7 @@ For a complete list of available news types, see [Enumerated parameters > News t
-**word_count_min:** `typing.Optional[int]` — The minimum number of words an article must contain. To be used for avoiding articles with small content. +**word_count_min:** `typing.Optional[WordCountMin]`
@@ -449,7 +407,7 @@ For a complete list of available news types, see [Enumerated parameters > News t
-**word_count_max:** `typing.Optional[int]` — The maximum number of words an article can contain. To be used for avoiding articles with large content. +**word_count_max:** `typing.Optional[WordCountMax]`
@@ -457,11 +415,7 @@ For a complete list of available news types, see [Enumerated parameters > News t
-**page:** `typing.Optional[int]` - -The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - -For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). +**page:** `typing.Optional[Page]`
@@ -469,7 +423,7 @@ For details, see [How to paginate large datasets](https://www.newscatcherapi.com
-**page_size:** `typing.Optional[int]` — The number of articles to return per page. +**page_size:** `typing.Optional[PageSize]`
@@ -477,11 +431,7 @@ For details, see [How to paginate large datasets](https://www.newscatcherapi.com
-**clustering_enabled:** `typing.Optional[bool]` - -Determines whether to group similar articles into clusters. If true, the API returns clustered results. - -To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). +**clustering_enabled:** `typing.Optional[ClusteringEnabled]`
@@ -489,16 +439,7 @@ To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-
-**clustering_variable:** `typing.Optional[SearchGetRequestClusteringVariable]` - -Specifies which part of the article to use for determining similarity when clustering. - -Possible values are: -- `content`: Uses the full article content (default). -- `title`: Uses only the article title. -- `summary`: Uses the article summary. - -To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). +**clustering_variable:** `typing.Optional[ClusteringVariable]`
@@ -506,16 +447,7 @@ To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-
-**clustering_threshold:** `typing.Optional[float]` - -Sets the similarity threshold for grouping articles into clusters. A lower value creates more inclusive clusters, while a higher value requires greater similarity between articles. - -Examples: -- `0.3`: Results in larger, more diverse clusters. -- `0.6`: Balances cluster size and article similarity (default). -- `0.9`: Creates smaller, tightly related clusters. - -To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). +**clustering_threshold:** `typing.Optional[ClusteringThreshold]`
@@ -539,17 +471,7 @@ To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-
-**theme:** `typing.Optional[str]` - -Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - -Example: `"Finance, Tech"` - -**Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - -Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. +**theme:** `typing.Optional[Theme]`
@@ -557,15 +479,7 @@ Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`
-**not_theme:** `typing.Optional[str]` - -Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - -Example: `"Crime, Tech"` - -**Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**not_theme:** `typing.Optional[NotTheme]`
@@ -573,15 +487,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**org_entity_name:** `typing.Optional[str]` - -Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"Apple, Microsoft"` - -**Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**org_entity_name:** `typing.Optional[OrgEntityName]`
@@ -589,15 +495,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**per_entity_name:** `typing.Optional[str]` - -Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"Elon Musk, Jeff Bezos"` - -**Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**per_entity_name:** `typing.Optional[PerEntityName]`
@@ -605,15 +503,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**loc_entity_name:** `typing.Optional[str]` - -Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"California, New York"` - -**Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**loc_entity_name:** `typing.Optional[LocEntityName]`
@@ -621,15 +511,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**misc_entity_name:** `typing.Optional[str]` - -Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"Bitcoin, Blockchain"` - -**Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**misc_entity_name:** `typing.Optional[MiscEntityName]`
@@ -637,18 +519,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**title_sentiment_min:** `typing.Optional[float]` - -Filters articles based on the minimum sentiment score of their titles. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**title_sentiment_min:** `typing.Optional[TitleSentimentMin]`
@@ -656,18 +527,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**title_sentiment_max:** `typing.Optional[float]` - -Filters articles based on the maximum sentiment score of their titles. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**title_sentiment_max:** `typing.Optional[TitleSentimentMax]`
@@ -675,18 +535,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**content_sentiment_min:** `typing.Optional[float]` - -Filters articles based on the minimum sentiment score of their content. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**content_sentiment_min:** `typing.Optional[ContentSentimentMin]`
@@ -694,18 +543,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**content_sentiment_max:** `typing.Optional[float]` - -Filters articles based on the maximum sentiment score of their content. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**content_sentiment_max:** `typing.Optional[ContentSentimentMax]`
@@ -717,8 +555,6 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. -Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -731,9 +567,7 @@ To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCod **not_iptc_tags:** `typing.Optional[str]` -Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - -Example: `"20000205, 20000209"` +Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. @@ -747,9 +581,7 @@ To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCod **iab_tags:** `typing.Optional[str]` -Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - -Example: `"Business, Events"` +Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. @@ -763,9 +595,7 @@ To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/c **not_iab_tags:** `typing.Optional[str]` -Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - -Example: `"Agriculture, Metals"` +Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. @@ -781,11 +611,11 @@ To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/c Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: -- `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. +- `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. -Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` +Example: `custom_tags.industry="Manufacturing,Logistics"` -To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). +To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). @@ -793,11 +623,7 @@ To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/
-**exclude_duplicates:** `typing.Optional[bool]` - -If true, excludes duplicate and highly similar articles from the search results. If false, returns all relevant articles, including duplicates. - -To learn more, see [Articles deduplication](/docs/v3/documentation/guides-and-concepts/articles-deduplication). +**exclude_duplicates:** `typing.Optional[ExcludeDuplicates]`
@@ -805,7 +631,7 @@ To learn more, see [Articles deduplication](/docs/v3/documentation/guides-and-co
-**robots_compliant:** `typing.Optional[bool]` — If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. +**robots_compliant:** `typing.Optional[RobotsCompliant]`
@@ -825,7 +651,7 @@ To learn more, see [Articles deduplication](/docs/v3/documentation/guides-and-co
-
client.search.post(...) +
client.search.post(...) -> SearchPostResponse
@@ -837,7 +663,7 @@ To learn more, see [Articles deduplication](/docs/v3/documentation/guides-and-co
-Searches for articles based on specified criteria such as keyword, language, country, source, and more. +Searches for articles based on specified criteria such as keywords, language, country, source, and more.
@@ -852,25 +678,17 @@ Searches for articles based on specified criteria such as keyword, language, cou
```python -import datetime - from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment client = NewscatcherApi( - api_key="YOUR_API_KEY", + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) + client.search.post( - q="renewable energy", - predefined_sources=["top 50 US"], - lang=["en"], - from_=datetime.datetime.fromisoformat( - "2024-01-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-06-30 00:00:00+00:00", - ), - additional_domain_info=True, - is_news_domain=True, + q="\"supply chain\" AND Amazon NOT China", + page_size=1, ) ``` @@ -983,7 +801,7 @@ client.search.post(
-**from_:** `typing.Optional[From]` +**from:** `typing.Optional[From]`
@@ -1095,6 +913,14 @@ client.search.post(
+**all_links_text:** `typing.Optional[AllLinksText]` + +
+
+ +
+
+ **additional_domain_info:** `typing.Optional[AdditionalDomainInfo]`
@@ -1348,7 +1174,7 @@ client.search.post(
## LatestHeadlines -
client.latestheadlines.get(...) +
client.latest_headlines.latest_headlines_get(...) -> LatestHeadlinesGetResponse
@@ -1376,22 +1202,57 @@ Retrieves the latest headlines for the specified time period. You can filter res ```python from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment client = NewscatcherApi( - api_key="YOUR_API_KEY", + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) -client.latestheadlines.get( - predefined_sources="top 100 US, top 5 GB", + +client.latest_headlines.latest_headlines_get( + when="7d", + by_parse_date=True, + lang="en,es", + not_lang="fr,de", + countries="US,CA", + not_countries="UK,FR", + predefined_sources="top 50 US, top 20 GB", + sources="nytimes.com,finance.yahoo.com", + not_sources="cnn.com,wsj.com", + not_author_name="John Doe, Jane Doe", + ranked_only=True, + is_headline=True, + is_opinion=True, + is_paid_content=False, + parent_url="wsj.com/politics,wsj.com/tech", + all_links="https://aiindex.stanford.edu/report,https://www.stateof.ai", + all_domain_links="who.int,nih.gov", + all_links_text="Nvidia,Tesla", + word_count_min=300, + word_count_max=1000, + page=2, + page_size=50, + clustering_enabled=True, + clustering_threshold=0.6, include_translation_fields=True, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", + theme="Finance,Tech", + not_theme="Crime,Sports", + org_entity_name="\"Apple Inc\" OR Microsoft", + per_entity_name="\"Elon Musk\" OR \"Jeff Bezos\"", + loc_entity_name="\"San Francisco\" OR \"New York City\"", + misc_entity_name="AWS OR \"Microsoft Azure\"", + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, iptc_tags="20000199,20000209", not_iptc_tags="20000205,20000209", iab_tags="Business,Events", not_iab_tags="Agriculture,Metals", - custom_tags="Tag1,Tag2,Tag3", + custom_tags="Tag1,Tag2", + robots_compliant=True, ) ``` @@ -1408,15 +1269,15 @@ client.latestheadlines.get(
-**when:** `typing.Optional[str]` +**when:** `typing.Optional[When]` + +
+
-The time period for which you want to get the latest headlines. +
+
-Format examples: -- `7d`: Last seven days -- `30d`: Last 30 days -- `1h`: Last hour -- `24h`: Last 24 hours +**by_parse_date:** `typing.Optional[ByParseDate]`
@@ -1424,7 +1285,7 @@ Format examples:
-**by_parse_date:** `typing.Optional[bool]` — If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. +**sort_by:** `typing.Optional[SortBy]`
@@ -1434,11 +1295,9 @@ Format examples: **lang:** `typing.Optional[str]` -The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - -Example: `"en, es"` +The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. -To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). +To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang).
@@ -1450,9 +1309,7 @@ To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/ove The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. -Example: `"fr, de"` - -To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). +To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). @@ -1464,9 +1321,7 @@ To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/ove The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. -Example: `"US, CA"` - -To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). +To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). @@ -1478,9 +1333,7 @@ To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/over The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. -Example:`"US, CA"` - -To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). +To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). @@ -1492,13 +1345,9 @@ To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/over Predefined top news sources per country. -Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. +Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). -Examples: -- `"top 100 US"` -- `"top 33 AT"` -- `"top 50 US, top 20 GB"` -- `"top 33 AT, top 50 IT"` +Multiple countries with the number of top sources can be specified as a comma-separated string. @@ -1506,13 +1355,7 @@ Examples:
-**sources:** `typing.Optional[str]` - -One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - -Examples: -- `"nytimes.com"` -- `"theguardian.com, finance.yahoo.com"` +**sources:** `typing.Optional[str]` — One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string.
@@ -1520,11 +1363,7 @@ Examples:
-**not_sources:** `typing.Optional[str]` - -The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - -Example: `"cnn.com, wsj.com"` +**not_sources:** `typing.Optional[str]` — The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string.
@@ -1532,11 +1371,7 @@ Example: `"cnn.com, wsj.com"`
-**not_author_name:** `typing.Optional[str]` - -The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - -Example: `"John Doe, Jane Doe"` +**not_author_name:** `typing.Optional[str]` — The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string.
@@ -1544,7 +1379,7 @@ Example: `"John Doe, Jane Doe"`
-**ranked_only:** `typing.Optional[bool]` — If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. +**ranked_only:** `typing.Optional[RankedOnly]`
@@ -1552,7 +1387,7 @@ Example: `"John Doe, Jane Doe"`
-**is_headline:** `typing.Optional[bool]` — If true, only returns articles that were posted on the home page of a given news domain. +**is_headline:** `typing.Optional[IsHeadline]`
@@ -1560,7 +1395,7 @@ Example: `"John Doe, Jane Doe"`
-**is_opinion:** `typing.Optional[bool]` — If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. +**is_opinion:** `typing.Optional[IsOpinion]`
@@ -1568,7 +1403,7 @@ Example: `"John Doe, Jane Doe"`
-**is_paid_content:** `typing.Optional[bool]` — If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. +**is_paid_content:** `typing.Optional[IsPaidContent]`
@@ -1576,11 +1411,7 @@ Example: `"John Doe, Jane Doe"`
-**parent_url:** `typing.Optional[str]` - -The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - -Example: `"wsj.com/politics, wsj.com/tech"` +**parent_url:** `typing.Optional[str]` — The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string.
@@ -1592,9 +1423,7 @@ Example: `"wsj.com/politics, wsj.com/tech"` The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. -Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - -For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). +For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). @@ -1606,9 +1435,7 @@ For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-ur The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. -Example: `"who.int, nih.gov"` - -For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). +For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). @@ -1616,7 +1443,13 @@ For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-ur
-**word_count_min:** `typing.Optional[int]` — The minimum number of words an article must contain. To be used for avoiding articles with small content. +**all_links_text:** `typing.Optional[str]` + +The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. + +**Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. + +To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url).
@@ -1624,7 +1457,7 @@ For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-ur
-**word_count_max:** `typing.Optional[int]` — The maximum number of words an article can contain. To be used for avoiding articles with large content. +**word_count_min:** `typing.Optional[WordCountMin]`
@@ -1632,11 +1465,7 @@ For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-ur
-**page:** `typing.Optional[int]` - -The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - -For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). +**word_count_max:** `typing.Optional[WordCountMax]`
@@ -1644,7 +1473,7 @@ For details, see [How to paginate large datasets](https://www.newscatcherapi.com
-**page_size:** `typing.Optional[int]` — The number of articles to return per page. +**page:** `typing.Optional[Page]`
@@ -1652,11 +1481,7 @@ For details, see [How to paginate large datasets](https://www.newscatcherapi.com
-**clustering_enabled:** `typing.Optional[bool]` - -Determines whether to group similar articles into clusters. If true, the API returns clustered results. - -To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). +**page_size:** `typing.Optional[PageSize]`
@@ -1664,16 +1489,7 @@ To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-
-**clustering_variable:** `typing.Optional[LatestHeadlinesGetRequestClusteringVariable]` - -Specifies which part of the article to use for determining similarity when clustering. - -Possible values are: -- `content`: Uses the full article content (default). -- `title`: Uses only the article title. -- `summary`: Uses the article summary. - -To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). +**clustering_enabled:** `typing.Optional[ClusteringEnabled]`
@@ -1681,16 +1497,15 @@ To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-
-**clustering_threshold:** `typing.Optional[float]` - -Sets the similarity threshold for grouping articles into clusters. A lower value creates more inclusive clusters, while a higher value requires greater similarity between articles. +**clustering_variable:** `typing.Optional[ClusteringVariable]` + +
+
-Examples: -- `0.3`: Results in larger, more diverse clusters. -- `0.6`: Balances cluster size and article similarity (default). -- `0.9`: Creates smaller, tightly related clusters. +
+
-To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). +**clustering_threshold:** `typing.Optional[ClusteringThreshold]`
@@ -1722,17 +1537,7 @@ To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-
-**theme:** `typing.Optional[str]` - -Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - -Example: `"Finance, Tech"` - -**Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - -Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. +**theme:** `typing.Optional[Theme]`
@@ -1740,15 +1545,7 @@ Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`
-**not_theme:** `typing.Optional[str]` - -Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - -Example: `"Crime, Tech"` - -**Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**not_theme:** `typing.Optional[NotTheme]`
@@ -1756,15 +1553,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**org_entity_name:** `typing.Optional[str]` - -Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"Apple, Microsoft"` - -**Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**org_entity_name:** `typing.Optional[OrgEntityName]`
@@ -1772,15 +1561,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**per_entity_name:** `typing.Optional[str]` - -Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"Elon Musk, Jeff Bezos"` - -**Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**per_entity_name:** `typing.Optional[PerEntityName]`
@@ -1788,15 +1569,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**loc_entity_name:** `typing.Optional[str]` - -Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"California, New York"` - -**Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**loc_entity_name:** `typing.Optional[LocEntityName]`
@@ -1804,15 +1577,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**misc_entity_name:** `typing.Optional[str]` - -Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"Bitcoin, Blockchain"` - -**Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**misc_entity_name:** `typing.Optional[MiscEntityName]`
@@ -1820,18 +1585,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**title_sentiment_min:** `typing.Optional[float]` - -Filters articles based on the minimum sentiment score of their titles. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**title_sentiment_min:** `typing.Optional[TitleSentimentMin]`
@@ -1839,18 +1593,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**title_sentiment_max:** `typing.Optional[float]` - -Filters articles based on the maximum sentiment score of their titles. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**title_sentiment_max:** `typing.Optional[TitleSentimentMax]`
@@ -1858,18 +1601,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**content_sentiment_min:** `typing.Optional[float]` - -Filters articles based on the minimum sentiment score of their content. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**content_sentiment_min:** `typing.Optional[ContentSentimentMin]`
@@ -1877,18 +1609,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**content_sentiment_max:** `typing.Optional[float]` - -Filters articles based on the maximum sentiment score of their content. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**content_sentiment_max:** `typing.Optional[ContentSentimentMax]`
@@ -1900,8 +1621,6 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. -Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1914,9 +1633,7 @@ To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCod **not_iptc_tags:** `typing.Optional[str]` -Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - -Example: `"20000205, 20000209"` +Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. @@ -1930,9 +1647,7 @@ To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCod **iab_tags:** `typing.Optional[str]` -Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - -Example: `"Business, Events"` +Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. @@ -1946,9 +1661,7 @@ To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/c **not_iab_tags:** `typing.Optional[str]` -Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - -Example: `"Agriculture, Metals"` +Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. @@ -1964,11 +1677,11 @@ To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/c Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: -- `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. +- `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. -Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` +Example: `custom_tags.industry="Manufacturing,Logistics"` -To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). +To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). @@ -1976,7 +1689,7 @@ To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/
-**robots_compliant:** `typing.Optional[bool]` — If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. +**robots_compliant:** `typing.Optional[RobotsCompliant]`
@@ -1996,7 +1709,7 @@ To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/
-
client.latestheadlines.post(...) +
client.latest_headlines.latest_headlines_post(...) -> LatestHeadlinesPostResponse
@@ -2024,15 +1737,16 @@ Retrieves the latest headlines for the specified time period. You can filter res ```python from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment client = NewscatcherApi( - api_key="YOUR_API_KEY", + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) -client.latestheadlines.post( - lang="en", - predefined_sources=["top 50 US", "top 20 GB"], - is_opinion=False, - page_size=10, + +client.latest_headlines.latest_headlines_post( + when="7d", + page_size=1, ) ``` @@ -2185,6 +1899,14 @@ client.latestheadlines.post(
+**all_links_text:** `typing.Optional[AllLinksText]` + +
+
+ +
+
+ **word_count_min:** `typing.Optional[WordCountMin]`
@@ -2405,8 +2127,8 @@ client.latestheadlines.post(
-## Breaking News -
client.breaking_news.breaking_news_get(...) +## BreakingNews +
client.breaking_news.breaking_news_get(...) -> BreakingNewsResponseDto
@@ -2434,17 +2156,33 @@ Retrieves breaking news articles and sorts them based on specified criteria. ```python from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment client = NewscatcherApi( - api_key="YOUR_API_KEY", + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) + client.breaking_news.breaking_news_get( + ranked_only=True, + from_rank=100, + to_rank=100, + page=2, + page_size=50, top_n_articles=5, include_translation_fields=True, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", + theme="Finance,Tech", + not_theme="Crime,Sports", + org_entity_name="\"Apple Inc\" OR Microsoft", + per_entity_name="\"Elon Musk\" OR \"Jeff Bezos\"", + loc_entity_name="\"San Francisco\" OR \"New York City\"", + misc_entity_name="AWS OR \"Microsoft Azure\"", + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, ) ``` @@ -2461,12 +2199,7 @@ client.breaking_news.breaking_news_get(
-**sort_by:** `typing.Optional[BreakingNewsGetRequestSortBy]` - -The sorting order of the results. Possible values are: -- `relevancy`: The most relevant results first. -- `date`: The most recently published results first. -- `rank`: The results from the highest-ranked sources first. +**sort_by:** `typing.Optional[SortBy]`
@@ -2474,7 +2207,7 @@ The sorting order of the results. Possible values are:
-**ranked_only:** `typing.Optional[bool]` — If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. +**ranked_only:** `typing.Optional[RankedOnly]`
@@ -2482,7 +2215,7 @@ The sorting order of the results. Possible values are:
-**from_rank:** `typing.Optional[int]` — The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. +**from_rank:** `typing.Optional[FromRank]`
@@ -2490,7 +2223,7 @@ The sorting order of the results. Possible values are:
-**to_rank:** `typing.Optional[int]` — The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. +**to_rank:** `typing.Optional[ToRank]`
@@ -2498,11 +2231,7 @@ The sorting order of the results. Possible values are:
-**page:** `typing.Optional[int]` - -The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - -For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). +**page:** `typing.Optional[Page]`
@@ -2510,7 +2239,7 @@ For details, see [How to paginate large datasets](https://www.newscatcherapi.com
-**page_size:** `typing.Optional[int]` — The number of articles to return per page. +**page_size:** `typing.Optional[PageSize]`
@@ -2550,33 +2279,7 @@ For details, see [How to paginate large datasets](https://www.newscatcherapi.com
-**theme:** `typing.Optional[str]` - -Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - -Example: `"Finance, Tech"` - -**Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - -Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - -
-
- -
-
- -**not_theme:** `typing.Optional[str]` - -Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - -Example: `"Crime, Tech"` - -**Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**theme:** `typing.Optional[Theme]`
@@ -2584,15 +2287,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**org_entity_name:** `typing.Optional[str]` - -Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"Apple, Microsoft"` - -**Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**not_theme:** `typing.Optional[NotTheme]`
@@ -2600,15 +2295,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**per_entity_name:** `typing.Optional[str]` - -Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"Elon Musk, Jeff Bezos"` - -**Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**org_entity_name:** `typing.Optional[OrgEntityName]`
@@ -2616,15 +2303,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**loc_entity_name:** `typing.Optional[str]` - -Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"California, New York"` - -**Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**per_entity_name:** `typing.Optional[PerEntityName]`
@@ -2632,15 +2311,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**misc_entity_name:** `typing.Optional[str]` - -Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"Bitcoin, Blockchain"` - -**Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**loc_entity_name:** `typing.Optional[LocEntityName]`
@@ -2648,18 +2319,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**title_sentiment_min:** `typing.Optional[float]` - -Filters articles based on the minimum sentiment score of their titles. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**misc_entity_name:** `typing.Optional[MiscEntityName]`
@@ -2667,18 +2327,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**title_sentiment_max:** `typing.Optional[float]` - -Filters articles based on the maximum sentiment score of their titles. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**title_sentiment_min:** `typing.Optional[TitleSentimentMin]`
@@ -2686,18 +2335,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**content_sentiment_min:** `typing.Optional[float]` - -Filters articles based on the minimum sentiment score of their content. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**title_sentiment_max:** `typing.Optional[TitleSentimentMax]`
@@ -2705,18 +2343,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**content_sentiment_max:** `typing.Optional[float]` - -Filters articles based on the maximum sentiment score of their content. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**content_sentiment_min:** `typing.Optional[ContentSentimentMin]`
@@ -2724,7 +2351,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**robots_compliant:** `typing.Optional[bool]` — If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. +**content_sentiment_max:** `typing.Optional[ContentSentimentMax]`
@@ -2744,7 +2371,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-
client.breaking_news.breaking_news_post(...) +
client.breaking_news.breaking_news_post(...) -> BreakingNewsResponseDto
@@ -2772,15 +2399,17 @@ Retrieves breaking news articles and sorts them based on specified criteria. ```python from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment client = NewscatcherApi( - api_key="YOUR_API_KEY", + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) + client.breaking_news.breaking_news_post( sort_by="relevancy", - page=1, - page_size=100, - include_nlp_data=True, + ranked_only=True, + top_n_articles=1, ) ``` @@ -2949,15 +2578,7 @@ client.breaking_news.breaking_news_post(
-**content_sentient_max:** `typing.Optional[ContentSentimentMax]` - -
-
- -
-
- -**robots_compliant:** `typing.Optional[RobotsCompliant]` +**content_sentiment_max:** `typing.Optional[ContentSentimentMax]`
@@ -2978,7 +2599,7 @@ client.breaking_news.breaking_news_post(
## Authors -
client.authors.get(...) +
client.authors.get(...) -> AuthorsGetResponse
@@ -3005,33 +2626,59 @@ Searches for articles written by a specified author. You can filter results by l
```python -import datetime - from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment +import datetime client = NewscatcherApi( - api_key="YOUR_API_KEY", + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) + client.authors.get( author_name="Jane Smith", - predefined_sources="top 100 US, top 5 GB", - from_=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", - ), + not_author_name="John Doe, Jane Doe", + predefined_sources="top 50 US, top 20 GB", + sources="nytimes.com,finance.yahoo.com", + not_sources="cnn.com,wsj.com", + lang="en,es", + not_lang="fr,de", + countries="US,CA", + not_countries="UK,FR", + from_=datetime.datetime.fromisoformat("2024-07-01T00:00:00+00:00"), + to=datetime.datetime.fromisoformat("2024-01-01T00:00:00+00:00"), + published_date_precision="full", + by_parse_date=True, + ranked_only=True, + from_rank=100, + to_rank=100, + is_headline=True, + is_opinion=True, + is_paid_content=False, + parent_url="wsj.com/politics,wsj.com/tech", + all_links="https://aiindex.stanford.edu/report,https://www.stateof.ai", + all_domain_links="who.int,nih.gov", + all_links_text="Nvidia,Tesla", + word_count_min=300, + word_count_max=1000, + page=2, + page_size=50, include_translation_fields=True, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", - ner_name="Tesla", + theme="Finance,Tech", + not_theme="Crime,Sports", + ner_name="Tesla,Amazon", + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, iptc_tags="20000199,20000209", not_iptc_tags="20000205,20000209", iab_tags="Business,Events", not_iab_tags="Agriculture,Metals", - custom_tags="Tag1,Tag2,Tag3", + custom_tags="Tag1,Tag2", + robots_compliant=True, ) ``` @@ -3048,7 +2695,7 @@ client.authors.get(
-**author_name:** `str` — The name of the author to search for. This parameter returns exact matches only. +**author_name:** `AuthorName`
@@ -3056,11 +2703,7 @@ client.authors.get(
-**not_author_name:** `typing.Optional[str]` - -The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - -Example: `"John Doe, Jane Doe"` +**not_author_name:** `typing.Optional[str]` — The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string.
@@ -3072,13 +2715,9 @@ Example: `"John Doe, Jane Doe"` Predefined top news sources per country. -Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. +Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). -Examples: -- `"top 100 US"` -- `"top 33 AT"` -- `"top 50 US, top 20 GB"` -- `"top 33 AT, top 50 IT"` +Multiple countries with the number of top sources can be specified as a comma-separated string.
@@ -3086,13 +2725,7 @@ Examples:
-**sources:** `typing.Optional[str]` - -One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - -Examples: -- `"nytimes.com"` -- `"theguardian.com, finance.yahoo.com"` +**sources:** `typing.Optional[str]` — One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string.
@@ -3100,11 +2733,7 @@ Examples:
-**not_sources:** `typing.Optional[str]` - -The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - -Example: `"cnn.com, wsj.com"` +**not_sources:** `typing.Optional[str]` — The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string.
@@ -3114,11 +2743,9 @@ Example: `"cnn.com, wsj.com"` **lang:** `typing.Optional[str]` -The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. +The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. -Example: `"en, es"` - -To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). +To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). @@ -3130,9 +2757,7 @@ To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/ove The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. -Example: `"fr, de"` - -To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). +To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). @@ -3144,9 +2769,7 @@ To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/ove The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. -Example: `"US, CA"` - -To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). +To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). @@ -3158,9 +2781,7 @@ To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/over The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. -Example:`"US, CA"` - -To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). +To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). @@ -3168,18 +2789,7 @@ To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/over
-**from_:** `typing.Optional[dt.datetime]` - -The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - -Formats with examples: -- YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` -- YYYY-MM-dd: `2024-07-01` -- YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` -- YYYY/mm/dd: `2024/07/01` -- English phrases: `7 day ago`, `today` - -**Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. +**from:** `typing.Optional[From]`
@@ -3187,18 +2797,7 @@ Formats with examples:
-**to:** `typing.Optional[dt.datetime]` - -The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - -Formats with examples: -- YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` -- YYYY-MM-dd: `2024-07-01` -- YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` -- YYYY/mm/dd: `2024/07/01` -- English phrases: `1 day ago`, `now` - -**Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. +**to:** `typing.Optional[To]`
@@ -3206,12 +2805,7 @@ Formats with examples:
-**published_date_precision:** `typing.Optional[AuthorsGetRequestPublishedDatePrecision]` - -The precision of the published date. There are three types: -- `full`: The day and time of an article is correctly identified with the appropriate timezone. -- `timezone unknown`: The day and time of an article is correctly identified without timezone. -- `date`: Only the day is identified without an exact time. +**published_date_precision:** `typing.Optional[PublishedDatePrecision]`
@@ -3219,7 +2813,7 @@ The precision of the published date. There are three types:
-**by_parse_date:** `typing.Optional[bool]` — If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. +**by_parse_date:** `typing.Optional[ByParseDate]`
@@ -3227,12 +2821,7 @@ The precision of the published date. There are three types:
-**sort_by:** `typing.Optional[AuthorsGetRequestSortBy]` - -The sorting order of the results. Possible values are: -- `relevancy`: The most relevant results first. -- `date`: The most recently published results first. -- `rank`: The results from the highest-ranked sources first. +**sort_by:** `typing.Optional[SortBy]`
@@ -3240,7 +2829,7 @@ The sorting order of the results. Possible values are:
-**ranked_only:** `typing.Optional[bool]` — If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. +**ranked_only:** `typing.Optional[RankedOnly]`
@@ -3248,7 +2837,7 @@ The sorting order of the results. Possible values are:
-**from_rank:** `typing.Optional[int]` — The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. +**from_rank:** `typing.Optional[FromRank]`
@@ -3256,7 +2845,7 @@ The sorting order of the results. Possible values are:
-**to_rank:** `typing.Optional[int]` — The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. +**to_rank:** `typing.Optional[ToRank]`
@@ -3264,7 +2853,7 @@ The sorting order of the results. Possible values are:
-**is_headline:** `typing.Optional[bool]` — If true, only returns articles that were posted on the home page of a given news domain. +**is_headline:** `typing.Optional[IsHeadline]`
@@ -3272,7 +2861,7 @@ The sorting order of the results. Possible values are:
-**is_opinion:** `typing.Optional[bool]` — If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. +**is_opinion:** `typing.Optional[IsOpinion]`
@@ -3280,7 +2869,7 @@ The sorting order of the results. Possible values are:
-**is_paid_content:** `typing.Optional[bool]` — If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. +**is_paid_content:** `typing.Optional[IsPaidContent]`
@@ -3288,11 +2877,7 @@ The sorting order of the results. Possible values are:
-**parent_url:** `typing.Optional[str]` - -The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - -Example: `"wsj.com/politics, wsj.com/tech"` +**parent_url:** `typing.Optional[str]` — The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string.
@@ -3304,9 +2889,7 @@ Example: `"wsj.com/politics, wsj.com/tech"` The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. -Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - -For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). +For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). @@ -3318,9 +2901,7 @@ For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-ur The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. -Example: `"who.int, nih.gov"` - -For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). +For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). @@ -3328,7 +2909,13 @@ For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-ur
-**word_count_min:** `typing.Optional[int]` — The minimum number of words an article must contain. To be used for avoiding articles with small content. +**all_links_text:** `typing.Optional[str]` + +The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. + +**Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. + +To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url).
@@ -3336,7 +2923,7 @@ For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-ur
-**word_count_max:** `typing.Optional[int]` — The maximum number of words an article can contain. To be used for avoiding articles with large content. +**word_count_min:** `typing.Optional[WordCountMin]`
@@ -3344,11 +2931,15 @@ For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-ur
-**page:** `typing.Optional[int]` +**word_count_max:** `typing.Optional[WordCountMax]` + +
+
-The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. +
+
-For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). +**page:** `typing.Optional[Page]`
@@ -3356,7 +2947,7 @@ For details, see [How to paginate large datasets](https://www.newscatcherapi.com
-**page_size:** `typing.Optional[int]` — The number of articles to return per page. +**page_size:** `typing.Optional[PageSize]`
@@ -3388,17 +2979,7 @@ For details, see [How to paginate large datasets](https://www.newscatcherapi.com
-**theme:** `typing.Optional[str]` - -Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - -Example: `"Finance, Tech"` - -**Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - -Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. +**theme:** `typing.Optional[Theme]`
@@ -3406,15 +2987,7 @@ Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`
-**not_theme:** `typing.Optional[str]` - -Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - -Example: `"Crime, Tech"` - -**Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**not_theme:** `typing.Optional[NotTheme]`
@@ -3422,11 +2995,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**ner_name:** `typing.Optional[str]` - -The name of person, organization, location, product or other named entity to search for. To specify multiple names use a comma-separated string. - -Example: `"Tesla, Amazon"` +**ner_name:** `typing.Optional[NerName]`
@@ -3434,18 +3003,7 @@ Example: `"Tesla, Amazon"`
-**title_sentiment_min:** `typing.Optional[float]` - -Filters articles based on the minimum sentiment score of their titles. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**title_sentiment_min:** `typing.Optional[TitleSentimentMin]`
@@ -3453,18 +3011,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**title_sentiment_max:** `typing.Optional[float]` - -Filters articles based on the maximum sentiment score of their titles. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**title_sentiment_max:** `typing.Optional[TitleSentimentMax]`
@@ -3472,18 +3019,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**content_sentiment_min:** `typing.Optional[float]` - -Filters articles based on the minimum sentiment score of their content. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**content_sentiment_min:** `typing.Optional[ContentSentimentMin]`
@@ -3491,18 +3027,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**content_sentiment_max:** `typing.Optional[float]` - -Filters articles based on the maximum sentiment score of their content. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**content_sentiment_max:** `typing.Optional[ContentSentimentMax]`
@@ -3514,8 +3039,6 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. -Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -3528,9 +3051,7 @@ To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCod **not_iptc_tags:** `typing.Optional[str]` -Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - -Example: `"20000205, 20000209"` +Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. @@ -3544,9 +3065,7 @@ To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCod **iab_tags:** `typing.Optional[str]` -Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - -Example: `"Business, Events"` +Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. @@ -3560,9 +3079,7 @@ To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/c **not_iab_tags:** `typing.Optional[str]` -Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - -Example: `"Agriculture, Metals"` +Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. @@ -3578,11 +3095,11 @@ To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/c Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: -- `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. +- `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. -Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` +Example: `custom_tags.industry="Manufacturing,Logistics"` -To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). +To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). @@ -3590,7 +3107,7 @@ To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/
-**robots_compliant:** `typing.Optional[bool]` — If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. +**robots_compliant:** `typing.Optional[RobotsCompliant]`
@@ -3610,7 +3127,7 @@ To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/
-
client.authors.post(...) +
client.authors.post(...) -> AuthorsPostResponse
@@ -3637,23 +3154,16 @@ Searches for articles by author. You can filter results by language, country, so
```python -import datetime - from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment client = NewscatcherApi( - api_key="YOUR_API_KEY", + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) + client.authors.post( - author_name="Joanna Stern", - sources=["wsj.com", "nytimes.com"], - lang="en", - from_=datetime.datetime.fromisoformat( - "2024-01-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-06-30 00:00:00+00:00", - ), + author_name="David Muir", ) ``` @@ -3742,7 +3252,7 @@ client.authors.post(
-**from_:** `typing.Optional[From]` +**from:** `typing.Optional[From]`
@@ -3854,6 +3364,14 @@ client.authors.post(
+**all_links_text:** `typing.Optional[AllLinksText]` + +
+
+ +
+
+ **word_count_min:** `typing.Optional[WordCountMin]`
@@ -4026,8 +3544,8 @@ client.authors.post(
-## SearchLink -
client.search_link.search_url_get(...) +## SearchByLink +
client.search_by_link.search_by_link_get(...) -> SearchResponseDto
@@ -4054,1124 +3572,44 @@ Searches for articles based on specified links or IDs. You can filter results by
```python -import datetime - from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment +import datetime client = NewscatcherApi( - api_key="YOUR_API_KEY", -) -client.search_link.search_url_get( - from_=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-01-01 00:00:00+00:00", - ), + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) -``` -
-
- - - -#### ⚙️ Parameters - -
-
- -
-
- -**ids:** `typing.Optional[str]` - -The Newscatcher article ID (corresponds to the `_id` field in API response) or a list of article IDs to search for. To specify multiple IDs, use a comma-separated string. - -Example: `"1234567890abcdef, abcdef1234567890"` - -**Caution**: You can use either the `links` or the `ids` parameter, but not both at the same time. - -
-
- -
-
- -**links:** `typing.Optional[str]` - -The article link or list of article links to search for. To specify multiple links, use a comma-separated string. - -Example: `"https://example.com/article1, https://example.com/article2"` - -**Caution**: You can use either the `links` or the `ids` parameter, but not both at the same time. - -
-
- -
-
- -**from_:** `typing.Optional[From]` - -
-
- -
-
- -**to:** `typing.Optional[To]` - -
-
+client.search_by_link.search_by_link_get( + ids="5f8d0d55b6e45e00179c6e7e", + links="https://nytimes.com/article1,https://bbc.com/article2", + from_=datetime.datetime.fromisoformat("2024-07-01T00:00:00+00:00"), + to=datetime.datetime.fromisoformat("2024-01-01T00:00:00+00:00"), + page=2, + page_size=50, + robots_compliant=True, +) -
-
- -**page:** `typing.Optional[int]` - -The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - -For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). - -
-
- -
-
- -**page_size:** `typing.Optional[int]` — The number of articles to return per page. - -
-
- -
-
- -**robots_compliant:** `typing.Optional[bool]` — If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - - - -
- -
client.search_link.search_url_post(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Searches for articles using their ID(s) or link(s). -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from newscatcher import NewscatcherApi - -client = NewscatcherApi( - api_key="YOUR_API_KEY", -) -client.search_link.search_url_post( - ids=[ - "8ea8a784568ffaa05cb6d1ab2d2e84dd", - "0146a551ef05ab1c494a55e806e3ce64", - ], - links=[ - "https://www.nytimes.com/2024/08/30/technology/ai-chatbot-chatgpt-manipulation.html", - "https://www.bbc.com/news/articles/c39k379grzlo", - ], -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**ids:** `typing.Optional[Ids]` - -
-
- -
-
- -**links:** `typing.Optional[Links]` - -
-
- -
-
- -**from_:** `typing.Optional[From]` - -The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text strings. The default time zone is UTC. - -Formats with examples: -- YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` -- YYYY-MM-dd: `2024-07-01` -- YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` -- YYYY/mm/dd: `2024/07/01` -- English phrases: `1 day ago`, `today` - -
-
- -
-
- -**to:** `typing.Optional[To]` - -The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text strings. The default time zone is UTC. - -Formats with examples: -- YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` -- YYYY-MM-dd: `2024-07-01` -- YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` -- YYYY/mm/dd: `2024/07/01` -- English phrases: `1 day ago`, `now` - -
-
- -
-
- -**page:** `typing.Optional[Page]` - -
-
- -
-
- -**page_size:** `typing.Optional[PageSize]` - -
-
- -
-
- -**robots_compliant:** `typing.Optional[RobotsCompliant]` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## SearchSimilar -
client.searchsimilar.get(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Searches for articles similar to a specified query. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -import datetime - -from newscatcher import NewscatcherApi - -client = NewscatcherApi( - api_key="YOUR_API_KEY", -) -client.searchsimilar.get( - q="technology AND (Apple OR Microsoft) NOT Google", - search_in="title_content, title_content_translated", - include_translation_fields=True, - similar_documents_fields="title,summary", - predefined_sources="top 100 US, top 5 GB", - from_=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", - ), - include_nlp_data=True, - has_nlp=True, - theme="Business,Finance", - not_theme="Crime", - ner_name="Tesla", - iptc_tags="20000199,20000209", - not_iptc_tags="20000205,20000209", - custom_tags="Tag1,Tag2,Tag3", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**q:** `str` - -The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - -- For an exact match, use double quotes. For example, `"technology news"`. -- Use `*` to search for any keyword. -- Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. -- Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - -For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). - -
-
- -
-
- -**search_in:** `typing.Optional[SearchIn]` - -
-
- -
-
- -**include_translation_fields:** `typing.Optional[IncludeTranslationFields]` - -
-
- -
-
- -**include_similar_documents:** `typing.Optional[bool]` — If true, includes similar documents in the response. - -
-
- -
-
- -**similar_documents_number:** `typing.Optional[int]` — The number of similar documents to return. - -
-
- -
-
- -**similar_documents_fields:** `typing.Optional[str]` — The fields to consider for finding similar documents. - -
-
- -
-
- -**predefined_sources:** `typing.Optional[str]` - -Predefined top news sources per country. - -Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. - -Examples: -- `"top 100 US"` -- `"top 33 AT"` -- `"top 50 US, top 20 GB"` -- `"top 33 AT, top 50 IT"` - -
-
- -
-
- -**sources:** `typing.Optional[str]` - -One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - -Examples: -- `"nytimes.com"` -- `"theguardian.com, finance.yahoo.com"` - -
-
- -
-
- -**not_sources:** `typing.Optional[str]` - -The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - -Example: `"cnn.com, wsj.com"` - -
-
- -
-
- -**lang:** `typing.Optional[str]` - -The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - -Example: `"en, es"` - -To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). - -
-
- -
-
- -**not_lang:** `typing.Optional[str]` - -The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - -Example: `"fr, de"` - -To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). - -
-
- -
-
- -**countries:** `typing.Optional[str]` - -The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - -Example: `"US, CA"` - -To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). - -
-
- -
-
- -**not_countries:** `typing.Optional[str]` - -The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - -Example:`"US, CA"` - -To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). - -
-
- -
-
- -**from_:** `typing.Optional[dt.datetime]` - -The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - -Formats with examples: -- YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` -- YYYY-MM-dd: `2024-07-01` -- YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` -- YYYY/mm/dd: `2024/07/01` -- English phrases: `7 day ago`, `today` - -**Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - -
-
- -
-
- -**to:** `typing.Optional[dt.datetime]` - -The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - -Formats with examples: -- YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` -- YYYY-MM-dd: `2024-07-01` -- YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` -- YYYY/mm/dd: `2024/07/01` -- English phrases: `1 day ago`, `now` - -**Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - -
-
- -
-
- -**by_parse_date:** `typing.Optional[bool]` — If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. - -
-
- -
-
- -**published_date_precision:** `typing.Optional[SearchSimilarGetRequestPublishedDatePrecision]` - -The precision of the published date. There are three types: -- `full`: The day and time of an article is correctly identified with the appropriate timezone. -- `timezone unknown`: The day and time of an article is correctly identified without timezone. -- `date`: Only the day is identified without an exact time. - -
-
- -
-
- -**sort_by:** `typing.Optional[SearchSimilarGetRequestSortBy]` - -The sorting order of the results. Possible values are: -- `relevancy`: The most relevant results first. -- `date`: The most recently published results first. -- `rank`: The results from the highest-ranked sources first. - -
-
- -
-
- -**ranked_only:** `typing.Optional[bool]` — If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. - -
-
- -
-
- -**from_rank:** `typing.Optional[int]` — The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. - -
-
- -
-
- -**to_rank:** `typing.Optional[int]` — The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. - -
-
- -
-
- -**is_headline:** `typing.Optional[bool]` — If true, only returns articles that were posted on the home page of a given news domain. - -
-
- -
-
- -**is_opinion:** `typing.Optional[bool]` — If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. - -
-
- -
-
- -**is_paid_content:** `typing.Optional[bool]` — If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. - -
-
- -
-
- -**parent_url:** `typing.Optional[str]` - -The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - -Example: `"wsj.com/politics, wsj.com/tech"` - -
-
- -
-
- -**all_links:** `typing.Optional[str]` - -The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - -Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - -For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). - -
-
- -
-
- -**all_domain_links:** `typing.Optional[str]` - -The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - -Example: `"who.int, nih.gov"` - -For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). - -
-
- -
-
- -**word_count_min:** `typing.Optional[int]` — The minimum number of words an article must contain. To be used for avoiding articles with small content. - -
-
- -
-
- -**word_count_max:** `typing.Optional[int]` — The maximum number of words an article can contain. To be used for avoiding articles with large content. - -
-
- -
-
- -**page:** `typing.Optional[int]` - -The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - -For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). - -
-
- -
-
- -**page_size:** `typing.Optional[int]` — The number of articles to return per page. - -
-
- -
-
- -**include_nlp_data:** `typing.Optional[IncludeNlpData]` - -
-
- -
-
- -**has_nlp:** `typing.Optional[HasNlp]` - -
-
- -
-
- -**theme:** `typing.Optional[str]` - -Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - -Example: `"Finance, Tech"` - -**Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - -Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - -
-
- -
-
- -**not_theme:** `typing.Optional[str]` - -Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - -Example: `"Crime, Tech"` - -**Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - -
-
- -
-
- -**ner_name:** `typing.Optional[str]` - -The name of person, organization, location, product or other named entity to search for. To specify multiple names use a comma-separated string. - -Example: `"Tesla, Amazon"` - -
-
- -
-
- -**title_sentiment_min:** `typing.Optional[float]` - -Filters articles based on the minimum sentiment score of their titles. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - -
-
- -
-
- -**title_sentiment_max:** `typing.Optional[float]` - -Filters articles based on the maximum sentiment score of their titles. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - -
-
- -
-
- -**content_sentiment_min:** `typing.Optional[float]` - -Filters articles based on the minimum sentiment score of their content. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - -
-
- -
-
- -**content_sentiment_max:** `typing.Optional[float]` - -Filters articles based on the maximum sentiment score of their content. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - -
-
- -
-
- -**iptc_tags:** `typing.Optional[str]` - -Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - -Example: `"20000199, 20000209"` - -**Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. - -To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). - -
-
- -
-
- -**not_iptc_tags:** `typing.Optional[str]` - -Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - -Example: `"20000205, 20000209"` - -**Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. - -To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). - -
-
- -
-
- -**custom_tags:** `typing.Optional[str]` - -Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - -- `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. - -Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` - -To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). - -
-
- -
-
- -**robots_compliant:** `typing.Optional[bool]` — If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.searchsimilar.post(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Searches for articles similar to the specified query. You can filter results by language, country, source, and more. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from newscatcher import NewscatcherApi - -client = NewscatcherApi( - api_key="YOUR_API_KEY", -) -client.searchsimilar.post( - q="artificial intelligence", - include_similar_documents=True, - similar_documents_number=5, -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**q:** `Q` - -
-
- -
-
- -**search_in:** `typing.Optional[SearchIn]` - -
-
- -
-
- -**include_translation_fields:** `typing.Optional[IncludeTranslationFields]` - -
-
- -
-
- -**include_similar_documents:** `typing.Optional[IncludeSimilarDocuments]` - -
-
- -
-
- -**similar_documents_number:** `typing.Optional[SimilarDocumentsNumber]` - -
-
- -
-
- -**similar_documents_fields:** `typing.Optional[SimilarDocumentsFields]` - -
-
- -
-
- -**predefined_sources:** `typing.Optional[PredefinedSources]` - -
-
- -
-
- -**sources:** `typing.Optional[Sources]` - -
-
- -
-
- -**not_sources:** `typing.Optional[NotSources]` - -
-
- -
-
- -**lang:** `typing.Optional[Lang]` - -
-
- -
-
- -**not_lang:** `typing.Optional[NotLang]` - -
-
- -
-
- -**countries:** `typing.Optional[Countries]` - -
-
- -
-
- -**not_countries:** `typing.Optional[NotCountries]` - -
-
- -
-
- -**from_:** `typing.Optional[From]` - -
-
- -
-
- -**to:** `typing.Optional[To]` - -
-
- -
-
- -**by_parse_date:** `typing.Optional[ByParseDate]` - -
-
- -
-
- -**published_date_precision:** `typing.Optional[PublishedDatePrecision]` - -
-
- -
-
- -**sort_by:** `typing.Optional[SortBy]` - -
-
- -
-
- -**ranked_only:** `typing.Optional[RankedOnly]` - -
-
- -
-
- -**from_rank:** `typing.Optional[FromRank]` - -
-
- -
-
- -**to_rank:** `typing.Optional[ToRank]` - +``` +
+
+#### ⚙️ Parameters +
-**is_headline:** `typing.Optional[IsHeadline]` - -
-
-
-**is_opinion:** `typing.Optional[IsOpinion]` +**ids:** `typing.Optional[str]` + +The Newscatcher article ID (corresponds to the `_id` field in API response) or a list of article IDs to search for. To specify multiple IDs, use a comma-separated string. + +**Caution**: You can use either the `links` or the `ids` parameter, but not both at the same time.
@@ -5179,7 +3617,11 @@ client.searchsimilar.post(
-**is_paid_content:** `typing.Optional[IsPaidContent]` +**links:** `typing.Optional[str]` + +The article link or list of article links to search for. To specify multiple links, use a comma-separated string. + +**Caution**: You can use either the `links` or the `ids` parameter, but not both at the same time.
@@ -5187,7 +3629,7 @@ client.searchsimilar.post(
-**parent_url:** `typing.Optional[ParentUrl]` +**from:** `typing.Optional[From]`
@@ -5195,7 +3637,7 @@ client.searchsimilar.post(
-**all_links:** `typing.Optional[AllLinks]` +**to:** `typing.Optional[To]`
@@ -5203,7 +3645,7 @@ client.searchsimilar.post(
-**all_domain_links:** `typing.Optional[AllDomainLinks]` +**page:** `typing.Optional[Page]`
@@ -5211,7 +3653,7 @@ client.searchsimilar.post(
-**word_count_min:** `typing.Optional[WordCountMin]` +**page_size:** `typing.Optional[PageSize]`
@@ -5219,7 +3661,7 @@ client.searchsimilar.post(
-**word_count_max:** `typing.Optional[WordCountMax]` +**robots_compliant:** `typing.Optional[RobotsCompliant]`
@@ -5227,71 +3669,72 @@ client.searchsimilar.post(
-**page:** `typing.Optional[Page]` +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+
+
-
-
-**page_size:** `typing.Optional[PageSize]` -
+
+
client.search_by_link.search_by_link_post(...) -> SearchResponseDto
-**include_nlp_data:** `typing.Optional[IncludeNlpData]` - -
-
+#### 📝 Description
-**has_nlp:** `typing.Optional[HasNlp]` - -
-
-
-**theme:** `typing.Optional[Theme]` - +Searches for articles using their ID(s) or link(s).
+ + + +#### 🔌 Usage
-**not_theme:** `typing.Optional[NotTheme]` - -
-
-
-**ner_name:** `typing.Optional[NerName]` - +```python +from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment + +client = NewscatcherApi( + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, +) + +client.search_by_link.search_by_link_post( + links="https://www.reuters.com/business/energy/oil-prices-up-after-israeli-attacks-oversupply-caps-gains-2025-09-10/", +) + +```
+ + + +#### ⚙️ Parameters
-**title_sentiment_min:** `typing.Optional[TitleSentimentMin]` - -
-
-
-**title_sentiment_max:** `typing.Optional[TitleSentimentMax]` +**ids:** `typing.Optional[Ids]`
@@ -5299,7 +3742,7 @@ client.searchsimilar.post(
-**content_sentiment_min:** `typing.Optional[ContentSentimentMin]` +**links:** `typing.Optional[Links]`
@@ -5307,7 +3750,16 @@ client.searchsimilar.post(
-**content_sentiment_max:** `typing.Optional[ContentSentimentMax]` +**from:** `typing.Optional[From]` + +The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text strings. The default time zone is UTC. + +Formats with examples: +- YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` +- YYYY-MM-dd: `2024-07-01` +- YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` +- YYYY/mm/dd: `2024/07/01` +- English phrases: `1 day ago`, `today`
@@ -5315,7 +3767,16 @@ client.searchsimilar.post(
-**iptc_tags:** `typing.Optional[IptcTags]` +**to:** `typing.Optional[To]` + +The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text strings. The default time zone is UTC. + +Formats with examples: +- YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` +- YYYY-MM-dd: `2024-07-01` +- YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` +- YYYY/mm/dd: `2024/07/01` +- English phrases: `1 day ago`, `now`
@@ -5323,7 +3784,7 @@ client.searchsimilar.post(
-**not_iptc_tags:** `typing.Optional[NotIptcTags]` +**page:** `typing.Optional[Page]`
@@ -5331,7 +3792,7 @@ client.searchsimilar.post(
-**custom_tags:** `typing.Optional[CustomTags]` +**page_size:** `typing.Optional[PageSize]`
@@ -5360,7 +3821,7 @@ client.searchsimilar.post(
## Sources -
client.sources.get(...) +
client.sources.get(...) -> SourcesResponseDto
@@ -5388,13 +3849,24 @@ Retrieves a list of sources based on specified criteria such as language, countr ```python from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment client = NewscatcherApi( - api_key="YOUR_API_KEY", + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) + client.sources.get( - predefined_sources="top 100 US, top 5 GB", + lang="en,es", + countries="US,CA", + predefined_sources="top 50 US, top 20 GB", + source_name="sport,tech", source_url="bbc.com", + include_additional_info=True, + is_news_domain=True, + news_type="General News Outlets,Tech News and Updates", + from_rank=100, + to_rank=100, ) ``` @@ -5413,11 +3885,9 @@ client.sources.get( **lang:** `typing.Optional[str]` -The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - -Example: `"en, es"` +The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. -To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). +To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang).
@@ -5429,9 +3899,7 @@ To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/ove The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. -Example: `"US, CA"` - -To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). +To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). @@ -5443,13 +3911,9 @@ To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/over Predefined top news sources per country. -Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. +Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). -Examples: -- `"top 100 US"` -- `"top 33 AT"` -- `"top 50 US, top 20 GB"` -- `"top 33 AT, top 50 IT"` +Multiple countries with the number of top sources can be specified as a comma-separated string. @@ -5461,8 +3925,6 @@ Examples: Word or phrase to search within the source names. To specify multiple values, use a comma-separated string. -Example: `"sport, tech"` - **Note**: The search doesn't require an exact match and returns sources containing the specified terms in their names. You can use any word or phrase, like `"sport"` or `"new york times"`. For example, `"sport"` returns sources such as `"Motorsport"`, `"Dot Esport"`, and `"Tuttosport"`. @@ -5484,15 +3946,7 @@ you can only use `include_additional_info` as an extra parameter.
-**include_additional_info:** `typing.Optional[bool]` - -If true, returns the following additional datapoints about each news source: -- `nb_articles_for_7d`: The number of articles published by the source in the last week. -- `country`: Source country of origin. -- `rank`: SEO rank. -- `is_news_domain`: Boolean indicating if the source is a news domain. -- `news_domain_type`: Type of news domain (e.g., "Original Content"). -- `news_type`: Category of news (e.g., "General News Outlets"). +**include_additional_info:** `typing.Optional[IncludeAdditionalInfo]`
@@ -5500,7 +3954,7 @@ If true, returns the following additional datapoints about each news source:
-**is_news_domain:** `typing.Optional[bool]` — If true, filters results to include only news domains. +**is_news_domain:** `typing.Optional[IsNewsDomain]`
@@ -5508,14 +3962,7 @@ If true, returns the following additional datapoints about each news source:
-**news_domain_type:** `typing.Optional[SourcesGetRequestNewsDomainType]` - -Filters results based on the news domain type. Possible values are: -- `Original Content`: Sources that produce their own content. -- `Aggregator`: Sources that collect content from various other sources. -- `Press Releases`: Sources primarily publishing press releases. -- `Republisher`: Sources that republish content from other sources. -- `Other`: Sources that don't fit into main categories. +**news_domain_type:** `typing.Optional[NewsDomainType]`
@@ -5527,9 +3974,7 @@ Filters results based on the news domain type. Possible values are: Filters results based on the news type. Multiple types can be specified using a comma-separated string. -Example: `"General News Outlets,Tech News and Updates"` - -For a complete list of available news types, see [Enumerated parameters > News type](/docs/v3/api-reference/overview/enumerated-parameters#news-type-news-type). +For a complete list of available news types, see [Enumerated parameters > News type](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#news-type-news-type). @@ -5537,7 +3982,7 @@ For a complete list of available news types, see [Enumerated parameters > News t
-**from_rank:** `typing.Optional[int]` — The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. +**from_rank:** `typing.Optional[FromRank]`
@@ -5545,7 +3990,7 @@ For a complete list of available news types, see [Enumerated parameters > News t
-**to_rank:** `typing.Optional[int]` — The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. +**to_rank:** `typing.Optional[ToRank]`
@@ -5565,7 +4010,7 @@ For a complete list of available news types, see [Enumerated parameters > News t
-
client.sources.post(...) +
client.sources.post(...) -> SourcesResponseDto
@@ -5593,16 +4038,15 @@ Retrieves the list of sources available in the database. You can filter the sour ```python from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment client = NewscatcherApi( - api_key="YOUR_API_KEY", + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) + client.sources.post( - predefined_sources=["top 50 US"], - include_additional_info=True, - is_news_domain=True, - news_domain_type="Original Content", - news_type="General News Outlets", + predefined_sources="top 10 US", ) ``` @@ -5720,7 +4164,7 @@ client.sources.post(
## Aggregation -
client.aggregation.get(...) +
client.aggregation.count_get(...) -> AggregationCountGetResponse
@@ -5747,29 +4191,59 @@ Retrieves the count of articles aggregated by day or hour based on various searc
```python -import datetime - from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment +import datetime client = NewscatcherApi( - api_key="YOUR_API_KEY", + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) -client.aggregation.get( - q="technology AND (Apple OR Microsoft) NOT Google", + +client.aggregation.count_get( + q="\"supply chain\" AND Amazon NOT China", search_in="title_content, title_content_translated", - predefined_sources="top 100 US, top 5 GB", - from_=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", - ), + predefined_sources="top 50 US, top 20 GB", + sources="nytimes.com,finance.yahoo.com", + not_sources="cnn.com,wsj.com", + lang="en,es", + not_lang="fr,de", + countries="US,CA", + not_countries="UK,FR", + not_author_name="John Doe, Jane Doe", + from_=datetime.datetime.fromisoformat("2024-07-01T00:00:00+00:00"), + to=datetime.datetime.fromisoformat("2024-01-01T00:00:00+00:00"), + published_date_precision="full", + by_parse_date=True, + ranked_only=True, + from_rank=100, + to_rank=100, + is_headline=True, + is_opinion=True, + is_paid_content=False, + parent_url="wsj.com/politics,wsj.com/tech", + all_links="https://aiindex.stanford.edu/report,https://www.stateof.ai", + all_domain_links="who.int,nih.gov", + all_links_text="Nvidia,Tesla", + word_count_min=300, + word_count_max=1000, + page=2, + page_size=50, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", + theme="Finance,Tech", + not_theme="Crime,Sports", + org_entity_name="\"Apple Inc\" OR Microsoft", + per_entity_name="\"Elon Musk\" OR \"Jeff Bezos\"", + loc_entity_name="\"San Francisco\" OR \"New York City\"", + misc_entity_name="AWS OR \"Microsoft Azure\"", + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, iptc_tags="20000199,20000209", not_iptc_tags="20000205,20000209", + robots_compliant=True, ) ``` @@ -5786,18 +4260,7 @@ client.aggregation.get(
-**q:** `str` - -The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - -- For an exact match, use double quotes. For example, `"technology news"`. -- Use `*` to search for any keyword. -- Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. -- Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - -For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). +**q:** `Q`
@@ -5825,13 +4288,9 @@ For more details, see [Advanced querying](/docs/v3/documentation/guides-and-conc Predefined top news sources per country. -Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. +Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). -Examples: -- `"top 100 US"` -- `"top 33 AT"` -- `"top 50 US, top 20 GB"` -- `"top 33 AT, top 50 IT"` +Multiple countries with the number of top sources can be specified as a comma-separated string.
@@ -5839,13 +4298,7 @@ Examples:
-**sources:** `typing.Optional[str]` - -One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - -Examples: -- `"nytimes.com"` -- `"theguardian.com, finance.yahoo.com"` +**sources:** `typing.Optional[str]` — One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string.
@@ -5853,11 +4306,7 @@ Examples:
-**not_sources:** `typing.Optional[str]` - -The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - -Example: `"cnn.com, wsj.com"` +**not_sources:** `typing.Optional[str]` — The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string.
@@ -5867,11 +4316,9 @@ Example: `"cnn.com, wsj.com"` **lang:** `typing.Optional[str]` -The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - -Example: `"en, es"` +The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. -To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). +To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). @@ -5883,9 +4330,7 @@ To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/ove The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. -Example: `"fr, de"` - -To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). +To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). @@ -5897,9 +4342,7 @@ To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/ove The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. -Example: `"US, CA"` - -To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). +To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). @@ -5911,9 +4354,7 @@ To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/over The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. -Example:`"US, CA"` - -To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). +To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). @@ -5921,11 +4362,7 @@ To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/over
-**not_author_name:** `typing.Optional[str]` - -The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - -Example: `"John Doe, Jane Doe"` +**not_author_name:** `typing.Optional[str]` — The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string.
@@ -5933,18 +4370,7 @@ Example: `"John Doe, Jane Doe"`
-**from_:** `typing.Optional[dt.datetime]` - -The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - -Formats with examples: -- YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` -- YYYY-MM-dd: `2024-07-01` -- YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` -- YYYY/mm/dd: `2024/07/01` -- English phrases: `7 day ago`, `today` - -**Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. +**from:** `typing.Optional[From]`
@@ -5952,18 +4378,7 @@ Formats with examples:
-**to:** `typing.Optional[dt.datetime]` - -The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - -Formats with examples: -- YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` -- YYYY-MM-dd: `2024-07-01` -- YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` -- YYYY/mm/dd: `2024/07/01` -- English phrases: `1 day ago`, `now` - -**Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. +**to:** `typing.Optional[To]`
@@ -5971,12 +4386,7 @@ Formats with examples:
-**published_date_precision:** `typing.Optional[AggregationGetRequestPublishedDatePrecision]` - -The precision of the published date. There are three types: -- `full`: The day and time of an article is correctly identified with the appropriate timezone. -- `timezone unknown`: The day and time of an article is correctly identified without timezone. -- `date`: Only the day is identified without an exact time. +**published_date_precision:** `typing.Optional[PublishedDatePrecision]`
@@ -5984,7 +4394,7 @@ The precision of the published date. There are three types:
-**by_parse_date:** `typing.Optional[bool]` — If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. +**by_parse_date:** `typing.Optional[ByParseDate]`
@@ -5992,12 +4402,7 @@ The precision of the published date. There are three types:
-**sort_by:** `typing.Optional[AggregationGetRequestSortBy]` - -The sorting order of the results. Possible values are: -- `relevancy`: The most relevant results first. -- `date`: The most recently published results first. -- `rank`: The results from the highest-ranked sources first. +**sort_by:** `typing.Optional[SortBy]`
@@ -6005,7 +4410,7 @@ The sorting order of the results. Possible values are:
-**ranked_only:** `typing.Optional[bool]` — If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. +**ranked_only:** `typing.Optional[RankedOnly]`
@@ -6013,7 +4418,7 @@ The sorting order of the results. Possible values are:
-**from_rank:** `typing.Optional[int]` — The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. +**from_rank:** `typing.Optional[FromRank]`
@@ -6021,7 +4426,7 @@ The sorting order of the results. Possible values are:
-**to_rank:** `typing.Optional[int]` — The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. +**to_rank:** `typing.Optional[ToRank]`
@@ -6029,7 +4434,7 @@ The sorting order of the results. Possible values are:
-**is_headline:** `typing.Optional[bool]` — If true, only returns articles that were posted on the home page of a given news domain. +**is_headline:** `typing.Optional[IsHeadline]`
@@ -6037,7 +4442,7 @@ The sorting order of the results. Possible values are:
-**is_opinion:** `typing.Optional[bool]` — If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. +**is_opinion:** `typing.Optional[IsOpinion]`
@@ -6045,7 +4450,7 @@ The sorting order of the results. Possible values are:
-**is_paid_content:** `typing.Optional[bool]` — If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. +**is_paid_content:** `typing.Optional[IsPaidContent]`
@@ -6053,11 +4458,7 @@ The sorting order of the results. Possible values are:
-**parent_url:** `typing.Optional[str]` - -The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - -Example: `"wsj.com/politics, wsj.com/tech"` +**parent_url:** `typing.Optional[str]` — The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string.
@@ -6069,9 +4470,7 @@ Example: `"wsj.com/politics, wsj.com/tech"` The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. -Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - -For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). +For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). @@ -6083,9 +4482,7 @@ For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-ur The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. -Example: `"who.int, nih.gov"` - -For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). +For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). @@ -6093,7 +4490,13 @@ For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-ur
-**word_count_min:** `typing.Optional[int]` — The minimum number of words an article must contain. To be used for avoiding articles with small content. +**all_links_text:** `typing.Optional[str]` + +The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. + +**Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. + +To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url).
@@ -6101,7 +4504,7 @@ For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-ur
-**word_count_max:** `typing.Optional[int]` — The maximum number of words an article can contain. To be used for avoiding articles with large content. +**word_count_min:** `typing.Optional[WordCountMin]`
@@ -6109,11 +4512,15 @@ For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-ur
-**page:** `typing.Optional[int]` +**word_count_max:** `typing.Optional[WordCountMax]` + +
+
-The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. +
+
-For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). +**page:** `typing.Optional[Page]`
@@ -6121,7 +4528,7 @@ For details, see [How to paginate large datasets](https://www.newscatcherapi.com
-**page_size:** `typing.Optional[int]` — The number of articles to return per page. +**page_size:** `typing.Optional[PageSize]`
@@ -6145,17 +4552,7 @@ For details, see [How to paginate large datasets](https://www.newscatcherapi.com
-**theme:** `typing.Optional[str]` - -Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - -Example: `"Finance, Tech"` - -**Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - -Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. +**theme:** `typing.Optional[Theme]`
@@ -6163,15 +4560,7 @@ Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`
-**not_theme:** `typing.Optional[str]` - -Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - -Example: `"Crime, Tech"` - -**Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**not_theme:** `typing.Optional[NotTheme]`
@@ -6179,15 +4568,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**org_entity_name:** `typing.Optional[str]` - -Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"Apple, Microsoft"` - -**Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**org_entity_name:** `typing.Optional[OrgEntityName]`
@@ -6195,15 +4576,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**per_entity_name:** `typing.Optional[str]` - -Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"Elon Musk, Jeff Bezos"` - -**Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**per_entity_name:** `typing.Optional[PerEntityName]`
@@ -6211,15 +4584,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**loc_entity_name:** `typing.Optional[str]` - -Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"California, New York"` - -**Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**loc_entity_name:** `typing.Optional[LocEntityName]`
@@ -6227,15 +4592,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**misc_entity_name:** `typing.Optional[str]` - -Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - -Example: `"Bitcoin, Blockchain"` - -**Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). +**misc_entity_name:** `typing.Optional[MiscEntityName]`
@@ -6243,18 +4600,7 @@ To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-en
-**title_sentiment_min:** `typing.Optional[float]` - -Filters articles based on the minimum sentiment score of their titles. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**title_sentiment_min:** `typing.Optional[TitleSentimentMin]`
@@ -6262,18 +4608,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**title_sentiment_max:** `typing.Optional[float]` - -Filters articles based on the maximum sentiment score of their titles. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**title_sentiment_max:** `typing.Optional[TitleSentimentMax]`
@@ -6281,18 +4616,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**content_sentiment_min:** `typing.Optional[float]` - -Filters articles based on the minimum sentiment score of their content. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**content_sentiment_min:** `typing.Optional[ContentSentimentMin]`
@@ -6300,18 +4624,7 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp
-**content_sentiment_max:** `typing.Optional[float]` - -Filters articles based on the maximum sentiment score of their content. - -Range is `-1.0` to `1.0`, where: -- Negative values indicate negative sentiment. -- Positive values indicate positive sentiment. -- Values close to 0 indicate neutral sentiment. - -**Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. - -To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). +**content_sentiment_max:** `typing.Optional[ContentSentimentMax]`
@@ -6323,8 +4636,6 @@ To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. -Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -6337,9 +4648,7 @@ To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCod **not_iptc_tags:** `typing.Optional[str]` -Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - -Example: `"20000205, 20000209"` +Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. @@ -6351,7 +4660,7 @@ To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCod
-**robots_compliant:** `typing.Optional[bool]` — If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. +**robots_compliant:** `typing.Optional[RobotsCompliant]`
@@ -6371,7 +4680,7 @@ To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCod
-
client.aggregation.post(...) +
client.aggregation.count_post(...) -> AggregationCountPostResponse
@@ -6398,23 +4707,17 @@ Retrieves the count of articles aggregated by day or hour based on various searc
```python -import datetime - from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment client = NewscatcherApi( - api_key="YOUR_API_KEY", + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) -client.aggregation.post( - q="renewable energy", + +client.aggregation.count_post( + q="\"supply chain\" AND Amazon NOT China", aggregation_by="day", - predefined_sources="top 50 US", - from_=datetime.datetime.fromisoformat( - "2024-01-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-06-30 00:00:00+00:00", - ), ) ``` @@ -6519,7 +4822,7 @@ client.aggregation.post(
-**from_:** `typing.Optional[From]` +**from:** `typing.Optional[From]`
@@ -6631,6 +4934,14 @@ client.aggregation.post(
+**all_links_text:** `typing.Optional[AllLinksText]` + +
+
+ +
+
+ **word_count_min:** `typing.Optional[WordCountMin]`
@@ -6796,7 +5107,7 @@ client.aggregation.post(
## Subscription -
client.subscription.get() +
client.subscription.get() -> SubscriptionResponseDto
@@ -6824,10 +5135,13 @@ Retrieves information about your subscription plan. ```python from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment client = NewscatcherApi( - api_key="YOUR_API_KEY", + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) + client.subscription.get() ``` @@ -6856,7 +5170,7 @@ client.subscription.get()
-
client.subscription.post() +
client.subscription.post() -> SubscriptionResponseDto
@@ -6884,10 +5198,13 @@ Retrieves information about your subscription plan. ```python from newscatcher import NewscatcherApi +from newscatcher.environment import NewscatcherApiEnvironment client = NewscatcherApi( - api_key="YOUR_API_KEY", + api_key="", + environment=NewscatcherApiEnvironment.DEFAULT, ) + client.subscription.post() ``` diff --git a/src/newscatcher/__init__.py b/src/newscatcher/__init__.py index 17df0a5..d36abec 100644 --- a/src/newscatcher/__init__.py +++ b/src/newscatcher/__init__.py @@ -2,194 +2,309 @@ # isort: skip_file -from .types import ( - AdditionalDomainInfo, - AdditionalDomainInfoEntity, - AdditionalSourceInfo, - AggregationBy, - AggregationCountResponseDto, - AggregationItem, - Aggregations, - AllDomainLinks, - AllLinks, - ArticleEntity, - ArticleEntityAllDomainLinks, - ArticleEntityAllLinks, - Articles, - AuthorName, - Authors, - BaseSearchResponseDto, - BreakingNewsArticleEntity, - BreakingNewsArticleEntityAllDomainLinks, - BreakingNewsArticleEntityAllLinks, - BreakingNewsEventEntity, - BreakingNewsResponseDto, - ByParseDate, - ClusterEntity, - ClusteredArticlesDto, - ClusteredSearchResponseDto, - ClusteringEnabled, - ClusteringThreshold, - ClusteringVariable, - ContentSentimentMax, - ContentSentimentMin, - Countries, - CustomTags, - Error, - ExcludeDuplicates, - FailedAggregationCountResponseDto, - FailedAuthorsResponseDto, - FailedSearchSimilarResponseDto, - From, - FromRank, - HasNlp, - IabTags, - Ids, - IncludeAdditionalInfo, - IncludeNlpData, - IncludeSimilarDocuments, - IncludeTranslationFields, - IptcTags, - IsHeadline, - IsNewsDomain, - IsOpinion, - IsPaidContent, - Journalists, - Lang, - Links, - LocEntityName, - MiscEntityName, - NamedEntityList, - NamedEntityListItem, - NerName, - NewsDomainType, - NewsType, - NlpDataEntity, - NotAuthorName, - NotCountries, - NotIabTags, - NotIptcTags, - NotLang, - NotSources, - NotTheme, - OrgEntityName, - Page, - PageSize, - ParentUrl, - PerEntityName, - PredefinedSources, - PublishedDatePrecision, - Q, - RankedOnly, - RobotsCompliant, - SearchIn, - SearchResponseDto, - SearchSimilarResponseDto, - SentimentScores, - SimilarArticleEntity, - SimilarDocument, - SimilarDocumentsFields, - SimilarDocumentsNumber, - SortBy, - SourceInfo, - SourceName, - SourceUrl, - Sources, - SourcesResponseDto, - SourcesResponseDtoSourcesItem, - SubscriptionResponseDto, - Theme, - TimeFrameCount, - TitleSentimentMax, - TitleSentimentMin, - To, - ToRank, - TopNArticles, - UserInputDto, - When, - WordCountMax, - WordCountMin, -) -from .errors import ( - BadRequestError, - ForbiddenError, - InternalServerError, - RequestTimeoutError, - TooManyRequestsError, - UnauthorizedError, - UnprocessableEntityError, -) -from . import ( - aggregation, - authors, - breaking_news, - latestheadlines, - search, - search_link, - searchsimilar, - sources, - subscription, -) -from .aggregation import ( - AggregationGetRequestPublishedDatePrecision, - AggregationGetRequestSortBy, - AggregationGetResponse, - AggregationPostResponse, -) -from .authors import ( - AuthorsGetRequestPublishedDatePrecision, - AuthorsGetRequestSortBy, - AuthorsGetResponse, - AuthorsPostResponse, -) -from .breaking_news import BreakingNewsGetRequestSortBy -from .client import AsyncNewscatcherApi, NewscatcherApi -from .environment import NewscatcherApiEnvironment -from .latestheadlines import ( - LatestHeadlinesGetRequestClusteringVariable, - LatestHeadlinesGetResponse, - LatestHeadlinesPostResponse, -) -from .search import ( - SearchGetRequestClusteringVariable, - SearchGetRequestNewsDomainType, - SearchGetRequestPublishedDatePrecision, - SearchGetRequestSortBy, - SearchGetResponse, - SearchPostResponse, -) -from .searchsimilar import ( - SearchSimilarGetRequestPublishedDatePrecision, - SearchSimilarGetRequestSortBy, - SearchSimilarGetResponse, - SearchSimilarPostResponse, -) -from .sources import SourcesGetRequestNewsDomainType -from .version import __version__ +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + AdditionalDomainInfo, + AdditionalDomainInfoEntity, + AdditionalSourceInfo, + AggregationBy, + AggregationCountResponseDto, + AggregationCountResponseDtoAggregations, + AggregationItem, + AllDomainLinks, + AllLinks, + AllLinksDataItem, + AllLinksText, + ArticleEntity, + ArticleEntityAllDomainLinks, + ArticleEntityAllLinks, + ArticleEntityAuthors, + ArticleEntityJournalists, + ArticlesDto, + AuthorName, + BaseSearchResponseDto, + BreakingNewsArticleEntity, + BreakingNewsArticleEntityAllDomainLinks, + BreakingNewsArticleEntityAllLinks, + BreakingNewsArticleEntityAuthors, + BreakingNewsArticleEntityJournalists, + BreakingNewsEventEntity, + BreakingNewsResponseDto, + ByParseDate, + ClusterEntity, + ClusteredArticlesDto, + ClusteredSearchResponseDto, + ClusteringEnabled, + ClusteringThreshold, + ClusteringVariable, + ContentSentimentMax, + ContentSentimentMin, + Countries, + CustomTags, + Error, + ExcludeDuplicates, + FailedAggregationCountResponseDto, + FailedAuthorsResponseDto, + From, + FromRank, + HasNlp, + IabTags, + Ids, + IncludeAdditionalInfo, + IncludeNlpData, + IncludeTranslationFields, + IptcTags, + IsHeadline, + IsNewsDomain, + IsOpinion, + IsPaidContent, + Lang, + Links, + LocEntityName, + MiscEntityName, + NamedEntityList, + NamedEntityListItem, + NerName, + NewsDomainType, + NewsType, + NlpDataEntity, + NotAuthorName, + NotCountries, + NotIabTags, + NotIptcTags, + NotLang, + NotSources, + NotTheme, + OrgEntityName, + Page, + PageSize, + ParentUrl, + PerEntityName, + PredefinedSources, + PublishedDatePrecision, + Q, + RankedOnly, + RobotsCompliant, + SearchIn, + SearchResponseDto, + SentimentScores, + SortBy, + SourceInfo, + SourceName, + SourceUrl, + Sources, + SourcesResponseDto, + SourcesResponseDtoSourcesItem, + SubscriptionResponseDto, + Theme, + TimeFrameCount, + TitleSentimentMax, + TitleSentimentMin, + To, + ToRank, + TopNArticles, + UserInputDto, + When, + WordCountMax, + WordCountMin, + ) + from .errors import ( + BadRequestError, + ForbiddenError, + InternalServerError, + RequestTimeoutError, + TooManyRequestsError, + UnauthorizedError, + UnprocessableEntityError, + ) + from . import aggregation, authors, breaking_news, latest_headlines, search, search_by_link, sources, subscription + from .aggregation import AggregationCountGetResponse, AggregationCountPostResponse + from .authors import AuthorsGetResponse, AuthorsPostResponse + from .client import AsyncNewscatcherApi, NewscatcherApi + from .environment import NewscatcherApiEnvironment + from .latest_headlines import LatestHeadlinesGetResponse, LatestHeadlinesPostResponse + from .search import SearchGetResponse, SearchPostResponse + from .version import __version__ +_dynamic_imports: typing.Dict[str, str] = { + "AdditionalDomainInfo": ".types", + "AdditionalDomainInfoEntity": ".types", + "AdditionalSourceInfo": ".types", + "AggregationBy": ".types", + "AggregationCountGetResponse": ".aggregation", + "AggregationCountPostResponse": ".aggregation", + "AggregationCountResponseDto": ".types", + "AggregationCountResponseDtoAggregations": ".types", + "AggregationItem": ".types", + "AllDomainLinks": ".types", + "AllLinks": ".types", + "AllLinksDataItem": ".types", + "AllLinksText": ".types", + "ArticleEntity": ".types", + "ArticleEntityAllDomainLinks": ".types", + "ArticleEntityAllLinks": ".types", + "ArticleEntityAuthors": ".types", + "ArticleEntityJournalists": ".types", + "ArticlesDto": ".types", + "AsyncNewscatcherApi": ".client", + "AuthorName": ".types", + "AuthorsGetResponse": ".authors", + "AuthorsPostResponse": ".authors", + "BadRequestError": ".errors", + "BaseSearchResponseDto": ".types", + "BreakingNewsArticleEntity": ".types", + "BreakingNewsArticleEntityAllDomainLinks": ".types", + "BreakingNewsArticleEntityAllLinks": ".types", + "BreakingNewsArticleEntityAuthors": ".types", + "BreakingNewsArticleEntityJournalists": ".types", + "BreakingNewsEventEntity": ".types", + "BreakingNewsResponseDto": ".types", + "ByParseDate": ".types", + "ClusterEntity": ".types", + "ClusteredArticlesDto": ".types", + "ClusteredSearchResponseDto": ".types", + "ClusteringEnabled": ".types", + "ClusteringThreshold": ".types", + "ClusteringVariable": ".types", + "ContentSentimentMax": ".types", + "ContentSentimentMin": ".types", + "Countries": ".types", + "CustomTags": ".types", + "Error": ".types", + "ExcludeDuplicates": ".types", + "FailedAggregationCountResponseDto": ".types", + "FailedAuthorsResponseDto": ".types", + "ForbiddenError": ".errors", + "From": ".types", + "FromRank": ".types", + "HasNlp": ".types", + "IabTags": ".types", + "Ids": ".types", + "IncludeAdditionalInfo": ".types", + "IncludeNlpData": ".types", + "IncludeTranslationFields": ".types", + "InternalServerError": ".errors", + "IptcTags": ".types", + "IsHeadline": ".types", + "IsNewsDomain": ".types", + "IsOpinion": ".types", + "IsPaidContent": ".types", + "Lang": ".types", + "LatestHeadlinesGetResponse": ".latest_headlines", + "LatestHeadlinesPostResponse": ".latest_headlines", + "Links": ".types", + "LocEntityName": ".types", + "MiscEntityName": ".types", + "NamedEntityList": ".types", + "NamedEntityListItem": ".types", + "NerName": ".types", + "NewsDomainType": ".types", + "NewsType": ".types", + "NewscatcherApi": ".client", + "NewscatcherApiEnvironment": ".environment", + "NlpDataEntity": ".types", + "NotAuthorName": ".types", + "NotCountries": ".types", + "NotIabTags": ".types", + "NotIptcTags": ".types", + "NotLang": ".types", + "NotSources": ".types", + "NotTheme": ".types", + "OrgEntityName": ".types", + "Page": ".types", + "PageSize": ".types", + "ParentUrl": ".types", + "PerEntityName": ".types", + "PredefinedSources": ".types", + "PublishedDatePrecision": ".types", + "Q": ".types", + "RankedOnly": ".types", + "RequestTimeoutError": ".errors", + "RobotsCompliant": ".types", + "SearchGetResponse": ".search", + "SearchIn": ".types", + "SearchPostResponse": ".search", + "SearchResponseDto": ".types", + "SentimentScores": ".types", + "SortBy": ".types", + "SourceInfo": ".types", + "SourceName": ".types", + "SourceUrl": ".types", + "Sources": ".types", + "SourcesResponseDto": ".types", + "SourcesResponseDtoSourcesItem": ".types", + "SubscriptionResponseDto": ".types", + "Theme": ".types", + "TimeFrameCount": ".types", + "TitleSentimentMax": ".types", + "TitleSentimentMin": ".types", + "To": ".types", + "ToRank": ".types", + "TooManyRequestsError": ".errors", + "TopNArticles": ".types", + "UnauthorizedError": ".errors", + "UnprocessableEntityError": ".errors", + "UserInputDto": ".types", + "When": ".types", + "WordCountMax": ".types", + "WordCountMin": ".types", + "__version__": ".version", + "aggregation": ".aggregation", + "authors": ".authors", + "breaking_news": ".breaking_news", + "latest_headlines": ".latest_headlines", + "search": ".search", + "search_by_link": ".search_by_link", + "sources": ".sources", + "subscription": ".subscription", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + __all__ = [ "AdditionalDomainInfo", "AdditionalDomainInfoEntity", "AdditionalSourceInfo", "AggregationBy", + "AggregationCountGetResponse", + "AggregationCountPostResponse", "AggregationCountResponseDto", - "AggregationGetRequestPublishedDatePrecision", - "AggregationGetRequestSortBy", - "AggregationGetResponse", + "AggregationCountResponseDtoAggregations", "AggregationItem", - "AggregationPostResponse", - "Aggregations", "AllDomainLinks", "AllLinks", + "AllLinksDataItem", + "AllLinksText", "ArticleEntity", "ArticleEntityAllDomainLinks", "ArticleEntityAllLinks", - "Articles", + "ArticleEntityAuthors", + "ArticleEntityJournalists", + "ArticlesDto", "AsyncNewscatcherApi", "AuthorName", - "Authors", - "AuthorsGetRequestPublishedDatePrecision", - "AuthorsGetRequestSortBy", "AuthorsGetResponse", "AuthorsPostResponse", "BadRequestError", @@ -197,8 +312,9 @@ "BreakingNewsArticleEntity", "BreakingNewsArticleEntityAllDomainLinks", "BreakingNewsArticleEntityAllLinks", + "BreakingNewsArticleEntityAuthors", + "BreakingNewsArticleEntityJournalists", "BreakingNewsEventEntity", - "BreakingNewsGetRequestSortBy", "BreakingNewsResponseDto", "ByParseDate", "ClusterEntity", @@ -215,7 +331,6 @@ "ExcludeDuplicates", "FailedAggregationCountResponseDto", "FailedAuthorsResponseDto", - "FailedSearchSimilarResponseDto", "ForbiddenError", "From", "FromRank", @@ -224,7 +339,6 @@ "Ids", "IncludeAdditionalInfo", "IncludeNlpData", - "IncludeSimilarDocuments", "IncludeTranslationFields", "InternalServerError", "IptcTags", @@ -232,9 +346,7 @@ "IsNewsDomain", "IsOpinion", "IsPaidContent", - "Journalists", "Lang", - "LatestHeadlinesGetRequestClusteringVariable", "LatestHeadlinesGetResponse", "LatestHeadlinesPostResponse", "Links", @@ -266,30 +378,16 @@ "RankedOnly", "RequestTimeoutError", "RobotsCompliant", - "SearchGetRequestClusteringVariable", - "SearchGetRequestNewsDomainType", - "SearchGetRequestPublishedDatePrecision", - "SearchGetRequestSortBy", "SearchGetResponse", "SearchIn", "SearchPostResponse", "SearchResponseDto", - "SearchSimilarGetRequestPublishedDatePrecision", - "SearchSimilarGetRequestSortBy", - "SearchSimilarGetResponse", - "SearchSimilarPostResponse", - "SearchSimilarResponseDto", "SentimentScores", - "SimilarArticleEntity", - "SimilarDocument", - "SimilarDocumentsFields", - "SimilarDocumentsNumber", "SortBy", "SourceInfo", "SourceName", "SourceUrl", "Sources", - "SourcesGetRequestNewsDomainType", "SourcesResponseDto", "SourcesResponseDtoSourcesItem", "SubscriptionResponseDto", @@ -311,10 +409,9 @@ "aggregation", "authors", "breaking_news", - "latestheadlines", + "latest_headlines", "search", - "search_link", - "searchsimilar", + "search_by_link", "sources", "subscription", ] diff --git a/src/newscatcher/aggregation/__init__.py b/src/newscatcher/aggregation/__init__.py index c4d8649..22a6c09 100644 --- a/src/newscatcher/aggregation/__init__.py +++ b/src/newscatcher/aggregation/__init__.py @@ -2,16 +2,36 @@ # isort: skip_file -from .types import ( - AggregationGetRequestPublishedDatePrecision, - AggregationGetRequestSortBy, - AggregationGetResponse, - AggregationPostResponse, -) - -__all__ = [ - "AggregationGetRequestPublishedDatePrecision", - "AggregationGetRequestSortBy", - "AggregationGetResponse", - "AggregationPostResponse", -] +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import AggregationCountGetResponse, AggregationCountPostResponse +_dynamic_imports: typing.Dict[str, str] = { + "AggregationCountGetResponse": ".types", + "AggregationCountPostResponse": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["AggregationCountGetResponse", "AggregationCountPostResponse"] diff --git a/src/newscatcher/aggregation/client.py b/src/newscatcher/aggregation/client.py index a512b41..84cce0f 100644 --- a/src/newscatcher/aggregation/client.py +++ b/src/newscatcher/aggregation/client.py @@ -1,6 +1,5 @@ # This file was auto-generated by Fern from our API Definition. -import datetime as dt import typing from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper @@ -8,6 +7,7 @@ from ..types.aggregation_by import AggregationBy from ..types.all_domain_links import AllDomainLinks from ..types.all_links import AllLinks +from ..types.all_links_text import AllLinksText from ..types.by_parse_date import ByParseDate from ..types.content_sentiment_max import ContentSentimentMax from ..types.content_sentiment_min import ContentSentimentMin @@ -50,10 +50,8 @@ from ..types.word_count_max import WordCountMax from ..types.word_count_min import WordCountMin from .raw_client import AsyncRawAggregationClient, RawAggregationClient -from .types.aggregation_get_request_published_date_precision import AggregationGetRequestPublishedDatePrecision -from .types.aggregation_get_request_sort_by import AggregationGetRequestSortBy -from .types.aggregation_get_response import AggregationGetResponse -from .types.aggregation_post_response import AggregationPostResponse +from .types.aggregation_count_get_response import AggregationCountGetResponse +from .types.aggregation_count_post_response import AggregationCountPostResponse # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -74,10 +72,10 @@ def with_raw_response(self) -> RawAggregationClient: """ return self._raw_client - def get( + def count_get( self, *, - q: str, + q: Q, aggregation_by: typing.Optional[AggregationBy] = None, search_in: typing.Optional[SearchIn] = None, predefined_sources: typing.Optional[str] = None, @@ -88,57 +86,48 @@ def get( countries: typing.Optional[str] = None, not_countries: typing.Optional[str] = None, not_author_name: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - published_date_precision: typing.Optional[AggregationGetRequestPublishedDatePrecision] = None, - by_parse_date: typing.Optional[bool] = None, - sort_by: typing.Optional[AggregationGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + from_: typing.Optional[From] = None, + to: typing.Optional[To] = None, + published_date_precision: typing.Optional[PublishedDatePrecision] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + all_links_text: typing.Optional[str] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AggregationGetResponse: + ) -> AggregationCountGetResponse: """ Retrieves the count of articles aggregated by day or hour based on various search criteria, such as keyword, language, country, and source. Parameters ---------- - q : str - The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - - - For an exact match, use double quotes. For example, `"technology news"`. - - Use `*` to search for any keyword. - - Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. - - Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - - For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). + q : Q aggregation_by : typing.Optional[AggregationBy] @@ -147,262 +136,116 @@ def get( predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` + from_ : typing.Optional[From] - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. + to : typing.Optional[To] - published_date_precision : typing.Optional[AggregationGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. + published_date_precision : typing.Optional[PublishedDatePrecision] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + by_parse_date : typing.Optional[ByParseDate] - sort_by : typing.Optional[AggregationGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. + sort_by : typing.Optional[SortBy] - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + word_count_min : typing.Optional[WordCountMin] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + word_count_max : typing.Optional[WordCountMax] - page_size : typing.Optional[int] - The number of articles to return per page. + page : typing.Optional[Page] + + page_size : typing.Optional[PageSize] include_nlp_data : typing.Optional[IncludeNlpData] has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. + theme : typing.Optional[Theme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + not_theme : typing.Optional[NotTheme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + org_entity_name : typing.Optional[OrgEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + per_entity_name : typing.Optional[PerEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + loc_entity_name : typing.Optional[LocEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + misc_entity_name : typing.Optional[MiscEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -410,21 +253,18 @@ def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AggregationGetResponse + AggregationCountGetResponse A successful response containing aggregation count results that match the search criteria. If no matches, returns a failed aggregation response according to the defined schema. Examples @@ -436,25 +276,57 @@ def get( client = NewscatcherApi( api_key="YOUR_API_KEY", ) - client.aggregation.get( - q="technology AND (Apple OR Microsoft) NOT Google", + client.aggregation.count_get( + q='"supply chain" AND Amazon NOT China', search_in="title_content, title_content_translated", - predefined_sources="top 100 US, top 5 GB", + predefined_sources="top 50 US, top 20 GB", + sources="nytimes.com,finance.yahoo.com", + not_sources="cnn.com,wsj.com", + lang="en,es", + not_lang="fr,de", + countries="US,CA", + not_countries="UK,FR", + not_author_name="John Doe, Jane Doe", from_=datetime.datetime.fromisoformat( "2024-07-01 00:00:00+00:00", ), to=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", + "2024-01-01 00:00:00+00:00", ), + published_date_precision="full", + by_parse_date=True, + ranked_only=True, + from_rank=100, + to_rank=100, + is_headline=True, + is_opinion=True, + is_paid_content=False, + parent_url="wsj.com/politics,wsj.com/tech", + all_links="https://aiindex.stanford.edu/report,https://www.stateof.ai", + all_domain_links="who.int,nih.gov", + all_links_text="Nvidia,Tesla", + word_count_min=300, + word_count_max=1000, + page=2, + page_size=50, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", + theme="Finance,Tech", + not_theme="Crime,Sports", + org_entity_name='"Apple Inc" OR Microsoft', + per_entity_name='"Elon Musk" OR "Jeff Bezos"', + loc_entity_name='"San Francisco" OR "New York City"', + misc_entity_name='AWS OR "Microsoft Azure"', + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, iptc_tags="20000199,20000209", not_iptc_tags="20000205,20000209", + robots_compliant=True, ) """ - _response = self._raw_client.get( + _response = self._raw_client.count_get( q=q, aggregation_by=aggregation_by, search_in=search_in, @@ -480,6 +352,7 @@ def get( parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, word_count_min=word_count_min, word_count_max=word_count_max, page=page, @@ -503,7 +376,7 @@ def get( ) return _response.data - def post( + def count_post( self, *, q: Q, @@ -531,6 +404,7 @@ def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, word_count_min: typing.Optional[WordCountMin] = OMIT, word_count_max: typing.Optional[WordCountMax] = OMIT, page: typing.Optional[Page] = OMIT, @@ -551,7 +425,7 @@ def post( not_iptc_tags: typing.Optional[NotIptcTags] = OMIT, robots_compliant: typing.Optional[RobotsCompliant] = OMIT, request_options: typing.Optional[RequestOptions] = None, - ) -> AggregationPostResponse: + ) -> AggregationCountPostResponse: """ Retrieves the count of articles aggregated by day or hour based on various search criteria, such as keyword, language, country, and source. @@ -607,6 +481,8 @@ def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + word_count_min : typing.Optional[WordCountMin] word_count_max : typing.Optional[WordCountMax] @@ -650,31 +526,22 @@ def post( Returns ------- - AggregationPostResponse + AggregationCountPostResponse A successful response containing aggregation count results that match the search criteria. If no matches, returns a failed aggregation response according to the defined schema. Examples -------- - import datetime - from newscatcher import NewscatcherApi client = NewscatcherApi( api_key="YOUR_API_KEY", ) - client.aggregation.post( - q="renewable energy", + client.aggregation.count_post( + q='"supply chain" AND Amazon NOT China', aggregation_by="day", - predefined_sources="top 50 US", - from_=datetime.datetime.fromisoformat( - "2024-01-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-06-30 00:00:00+00:00", - ), ) """ - _response = self._raw_client.post( + _response = self._raw_client.count_post( q=q, aggregation_by=aggregation_by, search_in=search_in, @@ -700,6 +567,7 @@ def post( parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, word_count_min=word_count_min, word_count_max=word_count_max, page=page, @@ -739,10 +607,10 @@ def with_raw_response(self) -> AsyncRawAggregationClient: """ return self._raw_client - async def get( + async def count_get( self, *, - q: str, + q: Q, aggregation_by: typing.Optional[AggregationBy] = None, search_in: typing.Optional[SearchIn] = None, predefined_sources: typing.Optional[str] = None, @@ -753,57 +621,48 @@ async def get( countries: typing.Optional[str] = None, not_countries: typing.Optional[str] = None, not_author_name: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - published_date_precision: typing.Optional[AggregationGetRequestPublishedDatePrecision] = None, - by_parse_date: typing.Optional[bool] = None, - sort_by: typing.Optional[AggregationGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + from_: typing.Optional[From] = None, + to: typing.Optional[To] = None, + published_date_precision: typing.Optional[PublishedDatePrecision] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + all_links_text: typing.Optional[str] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AggregationGetResponse: + ) -> AggregationCountGetResponse: """ Retrieves the count of articles aggregated by day or hour based on various search criteria, such as keyword, language, country, and source. Parameters ---------- - q : str - The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - - - For an exact match, use double quotes. For example, `"technology news"`. - - Use `*` to search for any keyword. - - Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. - - Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - - For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). + q : Q aggregation_by : typing.Optional[AggregationBy] @@ -812,262 +671,116 @@ async def get( predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` + from_ : typing.Optional[From] - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. + to : typing.Optional[To] - published_date_precision : typing.Optional[AggregationGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. + published_date_precision : typing.Optional[PublishedDatePrecision] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + by_parse_date : typing.Optional[ByParseDate] - sort_by : typing.Optional[AggregationGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. + sort_by : typing.Optional[SortBy] - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + word_count_min : typing.Optional[WordCountMin] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + word_count_max : typing.Optional[WordCountMax] - page_size : typing.Optional[int] - The number of articles to return per page. + page : typing.Optional[Page] + + page_size : typing.Optional[PageSize] include_nlp_data : typing.Optional[IncludeNlpData] has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. + theme : typing.Optional[Theme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + not_theme : typing.Optional[NotTheme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + org_entity_name : typing.Optional[OrgEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + per_entity_name : typing.Optional[PerEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + loc_entity_name : typing.Optional[LocEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + misc_entity_name : typing.Optional[MiscEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1075,21 +788,18 @@ async def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AggregationGetResponse + AggregationCountGetResponse A successful response containing aggregation count results that match the search criteria. If no matches, returns a failed aggregation response according to the defined schema. Examples @@ -1105,28 +815,60 @@ async def get( async def main() -> None: - await client.aggregation.get( - q="technology AND (Apple OR Microsoft) NOT Google", + await client.aggregation.count_get( + q='"supply chain" AND Amazon NOT China', search_in="title_content, title_content_translated", - predefined_sources="top 100 US, top 5 GB", + predefined_sources="top 50 US, top 20 GB", + sources="nytimes.com,finance.yahoo.com", + not_sources="cnn.com,wsj.com", + lang="en,es", + not_lang="fr,de", + countries="US,CA", + not_countries="UK,FR", + not_author_name="John Doe, Jane Doe", from_=datetime.datetime.fromisoformat( "2024-07-01 00:00:00+00:00", ), to=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", + "2024-01-01 00:00:00+00:00", ), + published_date_precision="full", + by_parse_date=True, + ranked_only=True, + from_rank=100, + to_rank=100, + is_headline=True, + is_opinion=True, + is_paid_content=False, + parent_url="wsj.com/politics,wsj.com/tech", + all_links="https://aiindex.stanford.edu/report,https://www.stateof.ai", + all_domain_links="who.int,nih.gov", + all_links_text="Nvidia,Tesla", + word_count_min=300, + word_count_max=1000, + page=2, + page_size=50, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", + theme="Finance,Tech", + not_theme="Crime,Sports", + org_entity_name='"Apple Inc" OR Microsoft', + per_entity_name='"Elon Musk" OR "Jeff Bezos"', + loc_entity_name='"San Francisco" OR "New York City"', + misc_entity_name='AWS OR "Microsoft Azure"', + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, iptc_tags="20000199,20000209", not_iptc_tags="20000205,20000209", + robots_compliant=True, ) asyncio.run(main()) """ - _response = await self._raw_client.get( + _response = await self._raw_client.count_get( q=q, aggregation_by=aggregation_by, search_in=search_in, @@ -1152,6 +894,7 @@ async def main() -> None: parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, word_count_min=word_count_min, word_count_max=word_count_max, page=page, @@ -1175,7 +918,7 @@ async def main() -> None: ) return _response.data - async def post( + async def count_post( self, *, q: Q, @@ -1203,6 +946,7 @@ async def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, word_count_min: typing.Optional[WordCountMin] = OMIT, word_count_max: typing.Optional[WordCountMax] = OMIT, page: typing.Optional[Page] = OMIT, @@ -1223,7 +967,7 @@ async def post( not_iptc_tags: typing.Optional[NotIptcTags] = OMIT, robots_compliant: typing.Optional[RobotsCompliant] = OMIT, request_options: typing.Optional[RequestOptions] = None, - ) -> AggregationPostResponse: + ) -> AggregationCountPostResponse: """ Retrieves the count of articles aggregated by day or hour based on various search criteria, such as keyword, language, country, and source. @@ -1279,6 +1023,8 @@ async def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + word_count_min : typing.Optional[WordCountMin] word_count_max : typing.Optional[WordCountMax] @@ -1322,13 +1068,12 @@ async def post( Returns ------- - AggregationPostResponse + AggregationCountPostResponse A successful response containing aggregation count results that match the search criteria. If no matches, returns a failed aggregation response according to the defined schema. Examples -------- import asyncio - import datetime from newscatcher import AsyncNewscatcherApi @@ -1338,22 +1083,15 @@ async def post( async def main() -> None: - await client.aggregation.post( - q="renewable energy", + await client.aggregation.count_post( + q='"supply chain" AND Amazon NOT China', aggregation_by="day", - predefined_sources="top 50 US", - from_=datetime.datetime.fromisoformat( - "2024-01-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-06-30 00:00:00+00:00", - ), ) asyncio.run(main()) """ - _response = await self._raw_client.post( + _response = await self._raw_client.count_post( q=q, aggregation_by=aggregation_by, search_in=search_in, @@ -1379,6 +1117,7 @@ async def main() -> None: parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, word_count_min=word_count_min, word_count_max=word_count_max, page=page, diff --git a/src/newscatcher/aggregation/raw_client.py b/src/newscatcher/aggregation/raw_client.py index 66a8064..42a6568 100644 --- a/src/newscatcher/aggregation/raw_client.py +++ b/src/newscatcher/aggregation/raw_client.py @@ -1,13 +1,12 @@ # This file was auto-generated by Fern from our API Definition. -import datetime as dt import typing from json.decoder import JSONDecodeError from ..core.api_error import ApiError from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.datetime_utils import serialize_datetime from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.parse_error import ParsingError from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..core.serialization import convert_and_respect_annotation_metadata @@ -21,6 +20,7 @@ from ..types.aggregation_by import AggregationBy from ..types.all_domain_links import AllDomainLinks from ..types.all_links import AllLinks +from ..types.all_links_text import AllLinksText from ..types.by_parse_date import ByParseDate from ..types.content_sentiment_max import ContentSentimentMax from ..types.content_sentiment_min import ContentSentimentMin @@ -63,10 +63,9 @@ from ..types.to_rank import ToRank from ..types.word_count_max import WordCountMax from ..types.word_count_min import WordCountMin -from .types.aggregation_get_request_published_date_precision import AggregationGetRequestPublishedDatePrecision -from .types.aggregation_get_request_sort_by import AggregationGetRequestSortBy -from .types.aggregation_get_response import AggregationGetResponse -from .types.aggregation_post_response import AggregationPostResponse +from .types.aggregation_count_get_response import AggregationCountGetResponse +from .types.aggregation_count_post_response import AggregationCountPostResponse +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -76,10 +75,10 @@ class RawAggregationClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def get( + def count_get( self, *, - q: str, + q: Q, aggregation_by: typing.Optional[AggregationBy] = None, search_in: typing.Optional[SearchIn] = None, predefined_sources: typing.Optional[str] = None, @@ -90,57 +89,48 @@ def get( countries: typing.Optional[str] = None, not_countries: typing.Optional[str] = None, not_author_name: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - published_date_precision: typing.Optional[AggregationGetRequestPublishedDatePrecision] = None, - by_parse_date: typing.Optional[bool] = None, - sort_by: typing.Optional[AggregationGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + from_: typing.Optional[From] = None, + to: typing.Optional[To] = None, + published_date_precision: typing.Optional[PublishedDatePrecision] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + all_links_text: typing.Optional[str] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[AggregationGetResponse]: + ) -> HttpResponse[AggregationCountGetResponse]: """ Retrieves the count of articles aggregated by day or hour based on various search criteria, such as keyword, language, country, and source. Parameters ---------- - q : str - The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - - - For an exact match, use double quotes. For example, `"technology news"`. - - Use `*` to search for any keyword. - - Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. - - Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - - For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). + q : Q aggregation_by : typing.Optional[AggregationBy] @@ -149,262 +139,116 @@ def get( predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` + from_ : typing.Optional[From] - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. + to : typing.Optional[To] - published_date_precision : typing.Optional[AggregationGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. + published_date_precision : typing.Optional[PublishedDatePrecision] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + by_parse_date : typing.Optional[ByParseDate] - sort_by : typing.Optional[AggregationGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. + sort_by : typing.Optional[SortBy] - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + word_count_min : typing.Optional[WordCountMin] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + word_count_max : typing.Optional[WordCountMax] - page_size : typing.Optional[int] - The number of articles to return per page. + page : typing.Optional[Page] + + page_size : typing.Optional[PageSize] include_nlp_data : typing.Optional[IncludeNlpData] has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. + theme : typing.Optional[Theme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + not_theme : typing.Optional[NotTheme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + org_entity_name : typing.Optional[OrgEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + per_entity_name : typing.Optional[PerEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + loc_entity_name : typing.Optional[LocEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + misc_entity_name : typing.Optional[MiscEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -412,21 +256,18 @@ def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[AggregationGetResponse] + HttpResponse[AggregationCountGetResponse] A successful response containing aggregation count results that match the search criteria. If no matches, returns a failed aggregation response according to the defined schema. """ _response = self._client_wrapper.httpx_client.request( @@ -444,8 +285,8 @@ def get( "countries": countries, "not_countries": not_countries, "not_author_name": not_author_name, - "from_": serialize_datetime(from_) if from_ is not None else None, - "to_": serialize_datetime(to) if to is not None else None, + "from_": convert_and_respect_annotation_metadata(object_=from_, annotation=From, direction="write"), + "to_": convert_and_respect_annotation_metadata(object_=to, annotation=To, direction="write"), "published_date_precision": published_date_precision, "by_parse_date": by_parse_date, "sort_by": sort_by, @@ -458,6 +299,7 @@ def get( "parent_url": parent_url, "all_links": all_links, "all_domain_links": all_domain_links, + "all_links_text": all_links_text, "word_count_min": word_count_min, "word_count_max": word_count_max, "page": page, @@ -483,9 +325,9 @@ def get( try: if 200 <= _response.status_code < 300: _data = typing.cast( - AggregationGetResponse, + AggregationCountGetResponse, parse_obj_as( - type_=AggregationGetResponse, # type: ignore + type_=AggregationCountGetResponse, # type: ignore object_=_response.json(), ), ) @@ -570,9 +412,13 @@ def get( _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 post( + def count_post( self, *, q: Q, @@ -600,6 +446,7 @@ def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, word_count_min: typing.Optional[WordCountMin] = OMIT, word_count_max: typing.Optional[WordCountMax] = OMIT, page: typing.Optional[Page] = OMIT, @@ -620,7 +467,7 @@ def post( not_iptc_tags: typing.Optional[NotIptcTags] = OMIT, robots_compliant: typing.Optional[RobotsCompliant] = OMIT, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[AggregationPostResponse]: + ) -> HttpResponse[AggregationCountPostResponse]: """ Retrieves the count of articles aggregated by day or hour based on various search criteria, such as keyword, language, country, and source. @@ -676,6 +523,8 @@ def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + word_count_min : typing.Optional[WordCountMin] word_count_max : typing.Optional[WordCountMax] @@ -719,7 +568,7 @@ def post( Returns ------- - HttpResponse[AggregationPostResponse] + HttpResponse[AggregationCountPostResponse] A successful response containing aggregation count results that match the search criteria. If no matches, returns a failed aggregation response according to the defined schema. """ _response = self._client_wrapper.httpx_client.request( @@ -771,16 +620,17 @@ def post( "all_domain_links": convert_and_respect_annotation_metadata( object_=all_domain_links, annotation=AllDomainLinks, direction="write" ), + "all_links_text": convert_and_respect_annotation_metadata( + object_=all_links_text, annotation=AllLinksText, direction="write" + ), "word_count_min": word_count_min, "word_count_max": word_count_max, "page": page, "page_size": page_size, "include_nlp_data": include_nlp_data, "has_nlp": has_nlp, - "theme": convert_and_respect_annotation_metadata(object_=theme, annotation=Theme, direction="write"), - "not_theme": convert_and_respect_annotation_metadata( - object_=not_theme, annotation=NotTheme, direction="write" - ), + "theme": theme, + "not_theme": not_theme, "ORG_entity_name": org_entity_name, "PER_entity_name": per_entity_name, "LOC_entity_name": loc_entity_name, @@ -806,9 +656,9 @@ def post( try: if 200 <= _response.status_code < 300: _data = typing.cast( - AggregationPostResponse, + AggregationCountPostResponse, parse_obj_as( - type_=AggregationPostResponse, # type: ignore + type_=AggregationCountPostResponse, # type: ignore object_=_response.json(), ), ) @@ -893,6 +743,10 @@ def post( _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) @@ -900,10 +754,10 @@ class AsyncRawAggregationClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def get( + async def count_get( self, *, - q: str, + q: Q, aggregation_by: typing.Optional[AggregationBy] = None, search_in: typing.Optional[SearchIn] = None, predefined_sources: typing.Optional[str] = None, @@ -914,57 +768,48 @@ async def get( countries: typing.Optional[str] = None, not_countries: typing.Optional[str] = None, not_author_name: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - published_date_precision: typing.Optional[AggregationGetRequestPublishedDatePrecision] = None, - by_parse_date: typing.Optional[bool] = None, - sort_by: typing.Optional[AggregationGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + from_: typing.Optional[From] = None, + to: typing.Optional[To] = None, + published_date_precision: typing.Optional[PublishedDatePrecision] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + all_links_text: typing.Optional[str] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[AggregationGetResponse]: + ) -> AsyncHttpResponse[AggregationCountGetResponse]: """ Retrieves the count of articles aggregated by day or hour based on various search criteria, such as keyword, language, country, and source. Parameters ---------- - q : str - The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - - - For an exact match, use double quotes. For example, `"technology news"`. - - Use `*` to search for any keyword. - - Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. - - Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - - For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). + q : Q aggregation_by : typing.Optional[AggregationBy] @@ -973,262 +818,116 @@ async def get( predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` + from_ : typing.Optional[From] - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. + to : typing.Optional[To] - published_date_precision : typing.Optional[AggregationGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. + published_date_precision : typing.Optional[PublishedDatePrecision] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + by_parse_date : typing.Optional[ByParseDate] - sort_by : typing.Optional[AggregationGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. + sort_by : typing.Optional[SortBy] - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + word_count_min : typing.Optional[WordCountMin] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + word_count_max : typing.Optional[WordCountMax] - page_size : typing.Optional[int] - The number of articles to return per page. + page : typing.Optional[Page] + + page_size : typing.Optional[PageSize] include_nlp_data : typing.Optional[IncludeNlpData] has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. + theme : typing.Optional[Theme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + not_theme : typing.Optional[NotTheme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + org_entity_name : typing.Optional[OrgEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + per_entity_name : typing.Optional[PerEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + loc_entity_name : typing.Optional[LocEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + misc_entity_name : typing.Optional[MiscEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1236,21 +935,18 @@ async def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[AggregationGetResponse] + AsyncHttpResponse[AggregationCountGetResponse] A successful response containing aggregation count results that match the search criteria. If no matches, returns a failed aggregation response according to the defined schema. """ _response = await self._client_wrapper.httpx_client.request( @@ -1268,8 +964,8 @@ async def get( "countries": countries, "not_countries": not_countries, "not_author_name": not_author_name, - "from_": serialize_datetime(from_) if from_ is not None else None, - "to_": serialize_datetime(to) if to is not None else None, + "from_": convert_and_respect_annotation_metadata(object_=from_, annotation=From, direction="write"), + "to_": convert_and_respect_annotation_metadata(object_=to, annotation=To, direction="write"), "published_date_precision": published_date_precision, "by_parse_date": by_parse_date, "sort_by": sort_by, @@ -1282,6 +978,7 @@ async def get( "parent_url": parent_url, "all_links": all_links, "all_domain_links": all_domain_links, + "all_links_text": all_links_text, "word_count_min": word_count_min, "word_count_max": word_count_max, "page": page, @@ -1307,9 +1004,9 @@ async def get( try: if 200 <= _response.status_code < 300: _data = typing.cast( - AggregationGetResponse, + AggregationCountGetResponse, parse_obj_as( - type_=AggregationGetResponse, # type: ignore + type_=AggregationCountGetResponse, # type: ignore object_=_response.json(), ), ) @@ -1394,9 +1091,13 @@ async def get( _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 post( + async def count_post( self, *, q: Q, @@ -1424,6 +1125,7 @@ async def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, word_count_min: typing.Optional[WordCountMin] = OMIT, word_count_max: typing.Optional[WordCountMax] = OMIT, page: typing.Optional[Page] = OMIT, @@ -1444,7 +1146,7 @@ async def post( not_iptc_tags: typing.Optional[NotIptcTags] = OMIT, robots_compliant: typing.Optional[RobotsCompliant] = OMIT, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[AggregationPostResponse]: + ) -> AsyncHttpResponse[AggregationCountPostResponse]: """ Retrieves the count of articles aggregated by day or hour based on various search criteria, such as keyword, language, country, and source. @@ -1500,6 +1202,8 @@ async def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + word_count_min : typing.Optional[WordCountMin] word_count_max : typing.Optional[WordCountMax] @@ -1543,7 +1247,7 @@ async def post( Returns ------- - AsyncHttpResponse[AggregationPostResponse] + AsyncHttpResponse[AggregationCountPostResponse] A successful response containing aggregation count results that match the search criteria. If no matches, returns a failed aggregation response according to the defined schema. """ _response = await self._client_wrapper.httpx_client.request( @@ -1595,16 +1299,17 @@ async def post( "all_domain_links": convert_and_respect_annotation_metadata( object_=all_domain_links, annotation=AllDomainLinks, direction="write" ), + "all_links_text": convert_and_respect_annotation_metadata( + object_=all_links_text, annotation=AllLinksText, direction="write" + ), "word_count_min": word_count_min, "word_count_max": word_count_max, "page": page, "page_size": page_size, "include_nlp_data": include_nlp_data, "has_nlp": has_nlp, - "theme": convert_and_respect_annotation_metadata(object_=theme, annotation=Theme, direction="write"), - "not_theme": convert_and_respect_annotation_metadata( - object_=not_theme, annotation=NotTheme, direction="write" - ), + "theme": theme, + "not_theme": not_theme, "ORG_entity_name": org_entity_name, "PER_entity_name": per_entity_name, "LOC_entity_name": loc_entity_name, @@ -1630,9 +1335,9 @@ async def post( try: if 200 <= _response.status_code < 300: _data = typing.cast( - AggregationPostResponse, + AggregationCountPostResponse, parse_obj_as( - type_=AggregationPostResponse, # type: ignore + type_=AggregationCountPostResponse, # type: ignore object_=_response.json(), ), ) @@ -1717,4 +1422,8 @@ async def post( _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) diff --git a/src/newscatcher/aggregation/types/__init__.py b/src/newscatcher/aggregation/types/__init__.py index 324ce07..ca0dbea 100644 --- a/src/newscatcher/aggregation/types/__init__.py +++ b/src/newscatcher/aggregation/types/__init__.py @@ -2,14 +2,37 @@ # isort: skip_file -from .aggregation_get_request_published_date_precision import AggregationGetRequestPublishedDatePrecision -from .aggregation_get_request_sort_by import AggregationGetRequestSortBy -from .aggregation_get_response import AggregationGetResponse -from .aggregation_post_response import AggregationPostResponse - -__all__ = [ - "AggregationGetRequestPublishedDatePrecision", - "AggregationGetRequestSortBy", - "AggregationGetResponse", - "AggregationPostResponse", -] +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .aggregation_count_get_response import AggregationCountGetResponse + from .aggregation_count_post_response import AggregationCountPostResponse +_dynamic_imports: typing.Dict[str, str] = { + "AggregationCountGetResponse": ".aggregation_count_get_response", + "AggregationCountPostResponse": ".aggregation_count_post_response", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["AggregationCountGetResponse", "AggregationCountPostResponse"] diff --git a/src/newscatcher/aggregation/types/aggregation_get_response.py b/src/newscatcher/aggregation/types/aggregation_count_get_response.py similarity index 70% rename from src/newscatcher/aggregation/types/aggregation_get_response.py rename to src/newscatcher/aggregation/types/aggregation_count_get_response.py index 315c283..9be1093 100644 --- a/src/newscatcher/aggregation/types/aggregation_get_response.py +++ b/src/newscatcher/aggregation/types/aggregation_count_get_response.py @@ -5,4 +5,4 @@ from ...types.aggregation_count_response_dto import AggregationCountResponseDto from ...types.failed_aggregation_count_response_dto import FailedAggregationCountResponseDto -AggregationGetResponse = typing.Union[AggregationCountResponseDto, FailedAggregationCountResponseDto] +AggregationCountGetResponse = typing.Union[AggregationCountResponseDto, FailedAggregationCountResponseDto] diff --git a/src/newscatcher/aggregation/types/aggregation_post_response.py b/src/newscatcher/aggregation/types/aggregation_count_post_response.py similarity index 70% rename from src/newscatcher/aggregation/types/aggregation_post_response.py rename to src/newscatcher/aggregation/types/aggregation_count_post_response.py index 1f4bd9a..b8488a9 100644 --- a/src/newscatcher/aggregation/types/aggregation_post_response.py +++ b/src/newscatcher/aggregation/types/aggregation_count_post_response.py @@ -5,4 +5,4 @@ from ...types.aggregation_count_response_dto import AggregationCountResponseDto from ...types.failed_aggregation_count_response_dto import FailedAggregationCountResponseDto -AggregationPostResponse = typing.Union[AggregationCountResponseDto, FailedAggregationCountResponseDto] +AggregationCountPostResponse = typing.Union[AggregationCountResponseDto, FailedAggregationCountResponseDto] diff --git a/src/newscatcher/aggregation/types/aggregation_get_request_published_date_precision.py b/src/newscatcher/aggregation/types/aggregation_get_request_published_date_precision.py deleted file mode 100644 index 222acca..0000000 --- a/src/newscatcher/aggregation/types/aggregation_get_request_published_date_precision.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -AggregationGetRequestPublishedDatePrecision = typing.Union[ - typing.Literal["full", "timezone unknown", "date"], typing.Any -] diff --git a/src/newscatcher/aggregation/types/aggregation_get_request_sort_by.py b/src/newscatcher/aggregation/types/aggregation_get_request_sort_by.py deleted file mode 100644 index 3c9a6fd..0000000 --- a/src/newscatcher/aggregation/types/aggregation_get_request_sort_by.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -AggregationGetRequestSortBy = typing.Union[typing.Literal["relevancy", "date", "rank"], typing.Any] diff --git a/src/newscatcher/authors/__init__.py b/src/newscatcher/authors/__init__.py index 420f07c..151c122 100644 --- a/src/newscatcher/authors/__init__.py +++ b/src/newscatcher/authors/__init__.py @@ -2,16 +2,33 @@ # isort: skip_file -from .types import ( - AuthorsGetRequestPublishedDatePrecision, - AuthorsGetRequestSortBy, - AuthorsGetResponse, - AuthorsPostResponse, -) - -__all__ = [ - "AuthorsGetRequestPublishedDatePrecision", - "AuthorsGetRequestSortBy", - "AuthorsGetResponse", - "AuthorsPostResponse", -] +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import AuthorsGetResponse, AuthorsPostResponse +_dynamic_imports: typing.Dict[str, str] = {"AuthorsGetResponse": ".types", "AuthorsPostResponse": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["AuthorsGetResponse", "AuthorsPostResponse"] diff --git a/src/newscatcher/authors/client.py b/src/newscatcher/authors/client.py index c2a1c65..3014a97 100644 --- a/src/newscatcher/authors/client.py +++ b/src/newscatcher/authors/client.py @@ -1,12 +1,12 @@ # This file was auto-generated by Fern from our API Definition. -import datetime as dt import typing from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.request_options import RequestOptions from ..types.all_domain_links import AllDomainLinks from ..types.all_links import AllLinks +from ..types.all_links_text import AllLinksText from ..types.author_name import AuthorName from ..types.by_parse_date import ByParseDate from ..types.content_sentiment_max import ContentSentimentMax @@ -49,8 +49,6 @@ from ..types.word_count_max import WordCountMax from ..types.word_count_min import WordCountMin from .raw_client import AsyncRawAuthorsClient, RawAuthorsClient -from .types.authors_get_request_published_date_precision import AuthorsGetRequestPublishedDatePrecision -from .types.authors_get_request_sort_by import AuthorsGetRequestSortBy from .types.authors_get_response import AuthorsGetResponse from .types.authors_post_response import AuthorsPostResponse @@ -76,7 +74,7 @@ def with_raw_response(self) -> RawAuthorsClient: def get( self, *, - author_name: str, + author_name: AuthorName, not_author_name: typing.Optional[str] = None, predefined_sources: typing.Optional[str] = None, sources: typing.Optional[str] = None, @@ -85,40 +83,41 @@ def get( not_lang: typing.Optional[str] = None, countries: typing.Optional[str] = None, not_countries: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - published_date_precision: typing.Optional[AuthorsGetRequestPublishedDatePrecision] = None, - by_parse_date: typing.Optional[bool] = None, - sort_by: typing.Optional[AuthorsGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + from_: typing.Optional[From] = None, + to: typing.Optional[To] = None, + published_date_precision: typing.Optional[PublishedDatePrecision] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + all_links_text: typing.Optional[str] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - ner_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + ner_name: typing.Optional[NerName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, iab_tags: typing.Optional[str] = None, not_iab_tags: typing.Optional[str] = None, custom_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AuthorsGetResponse: """ @@ -126,154 +125,93 @@ def get( Parameters ---------- - author_name : str - The name of the author to search for. This parameter returns exact matches only. + author_name : AuthorName not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). - - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` + from_ : typing.Optional[From] - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. + to : typing.Optional[To] - published_date_precision : typing.Optional[AuthorsGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. + published_date_precision : typing.Optional[PublishedDatePrecision] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + by_parse_date : typing.Optional[ByParseDate] - sort_by : typing.Optional[AuthorsGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. + sort_by : typing.Optional[SortBy] - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). + + word_count_min : typing.Optional[WordCountMin] - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + word_count_max : typing.Optional[WordCountMax] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + page : typing.Optional[Page] - page_size : typing.Optional[int] - The number of articles to return per page. + page_size : typing.Optional[PageSize] include_translation_fields : typing.Optional[IncludeTranslationFields] @@ -281,84 +219,23 @@ def get( has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - ner_name : typing.Optional[str] - The name of person, organization, location, product or other named entity to search for. To specify multiple names use a comma-separated string. - - Example: `"Tesla, Amazon"` - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + theme : typing.Optional[Theme] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + not_theme : typing.Optional[NotTheme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + ner_name : typing.Optional[NerName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -366,8 +243,6 @@ def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -375,8 +250,6 @@ def get( iab_tags : typing.Optional[str] Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - Example: `"Business, Events"` - **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -384,8 +257,6 @@ def get( not_iab_tags : typing.Optional[str] Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - Example: `"Agriculture, Metals"` - **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -393,14 +264,13 @@ def get( custom_tags : typing.Optional[str] Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. + - `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` + Example: `custom_tags.industry="Manufacturing,Logistics"` - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). + To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -421,24 +291,52 @@ def get( ) client.authors.get( author_name="Jane Smith", - predefined_sources="top 100 US, top 5 GB", + not_author_name="John Doe, Jane Doe", + predefined_sources="top 50 US, top 20 GB", + sources="nytimes.com,finance.yahoo.com", + not_sources="cnn.com,wsj.com", + lang="en,es", + not_lang="fr,de", + countries="US,CA", + not_countries="UK,FR", from_=datetime.datetime.fromisoformat( "2024-07-01 00:00:00+00:00", ), to=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", + "2024-01-01 00:00:00+00:00", ), + published_date_precision="full", + by_parse_date=True, + ranked_only=True, + from_rank=100, + to_rank=100, + is_headline=True, + is_opinion=True, + is_paid_content=False, + parent_url="wsj.com/politics,wsj.com/tech", + all_links="https://aiindex.stanford.edu/report,https://www.stateof.ai", + all_domain_links="who.int,nih.gov", + all_links_text="Nvidia,Tesla", + word_count_min=300, + word_count_max=1000, + page=2, + page_size=50, include_translation_fields=True, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", - ner_name="Tesla", + theme="Finance,Tech", + not_theme="Crime,Sports", + ner_name="Tesla,Amazon", + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, iptc_tags="20000199,20000209", not_iptc_tags="20000205,20000209", iab_tags="Business,Events", not_iab_tags="Agriculture,Metals", - custom_tags="Tag1,Tag2,Tag3", + custom_tags="Tag1,Tag2", + robots_compliant=True, ) """ _response = self._raw_client.get( @@ -465,6 +363,7 @@ def get( parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, word_count_min=word_count_min, word_count_max=word_count_max, page=page, @@ -515,6 +414,7 @@ def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, word_count_min: typing.Optional[WordCountMin] = OMIT, word_count_max: typing.Optional[WordCountMax] = OMIT, page: typing.Optional[Page] = OMIT, @@ -588,6 +488,8 @@ def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + word_count_min : typing.Optional[WordCountMin] word_count_max : typing.Optional[WordCountMax] @@ -638,23 +540,13 @@ def post( Examples -------- - import datetime - from newscatcher import NewscatcherApi client = NewscatcherApi( api_key="YOUR_API_KEY", ) client.authors.post( - author_name="Joanna Stern", - sources=["wsj.com", "nytimes.com"], - lang="en", - from_=datetime.datetime.fromisoformat( - "2024-01-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-06-30 00:00:00+00:00", - ), + author_name="David Muir", ) """ _response = self._raw_client.post( @@ -681,6 +573,7 @@ def post( parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, word_count_min=word_count_min, word_count_max=word_count_max, page=page, @@ -724,7 +617,7 @@ def with_raw_response(self) -> AsyncRawAuthorsClient: async def get( self, *, - author_name: str, + author_name: AuthorName, not_author_name: typing.Optional[str] = None, predefined_sources: typing.Optional[str] = None, sources: typing.Optional[str] = None, @@ -733,40 +626,41 @@ async def get( not_lang: typing.Optional[str] = None, countries: typing.Optional[str] = None, not_countries: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - published_date_precision: typing.Optional[AuthorsGetRequestPublishedDatePrecision] = None, - by_parse_date: typing.Optional[bool] = None, - sort_by: typing.Optional[AuthorsGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + from_: typing.Optional[From] = None, + to: typing.Optional[To] = None, + published_date_precision: typing.Optional[PublishedDatePrecision] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + all_links_text: typing.Optional[str] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - ner_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + ner_name: typing.Optional[NerName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, iab_tags: typing.Optional[str] = None, not_iab_tags: typing.Optional[str] = None, custom_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AuthorsGetResponse: """ @@ -774,154 +668,93 @@ async def get( Parameters ---------- - author_name : str - The name of the author to search for. This parameter returns exact matches only. + author_name : AuthorName not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` + from_ : typing.Optional[From] - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. + to : typing.Optional[To] - published_date_precision : typing.Optional[AuthorsGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. + published_date_precision : typing.Optional[PublishedDatePrecision] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + by_parse_date : typing.Optional[ByParseDate] - sort_by : typing.Optional[AuthorsGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. + sort_by : typing.Optional[SortBy] - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). + + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. + word_count_min : typing.Optional[WordCountMin] - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + word_count_max : typing.Optional[WordCountMax] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + page : typing.Optional[Page] - page_size : typing.Optional[int] - The number of articles to return per page. + page_size : typing.Optional[PageSize] include_translation_fields : typing.Optional[IncludeTranslationFields] @@ -929,84 +762,23 @@ async def get( has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - ner_name : typing.Optional[str] - The name of person, organization, location, product or other named entity to search for. To specify multiple names use a comma-separated string. - - Example: `"Tesla, Amazon"` - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + theme : typing.Optional[Theme] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + not_theme : typing.Optional[NotTheme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + ner_name : typing.Optional[NerName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1014,8 +786,6 @@ async def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1023,8 +793,6 @@ async def get( iab_tags : typing.Optional[str] Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - Example: `"Business, Events"` - **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -1032,8 +800,6 @@ async def get( not_iab_tags : typing.Optional[str] Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - Example: `"Agriculture, Metals"` - **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -1041,14 +807,13 @@ async def get( custom_tags : typing.Optional[str] Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. + - `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` + Example: `custom_tags.industry="Manufacturing,Logistics"` - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). + To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1073,24 +838,52 @@ async def get( async def main() -> None: await client.authors.get( author_name="Jane Smith", - predefined_sources="top 100 US, top 5 GB", + not_author_name="John Doe, Jane Doe", + predefined_sources="top 50 US, top 20 GB", + sources="nytimes.com,finance.yahoo.com", + not_sources="cnn.com,wsj.com", + lang="en,es", + not_lang="fr,de", + countries="US,CA", + not_countries="UK,FR", from_=datetime.datetime.fromisoformat( "2024-07-01 00:00:00+00:00", ), to=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", + "2024-01-01 00:00:00+00:00", ), + published_date_precision="full", + by_parse_date=True, + ranked_only=True, + from_rank=100, + to_rank=100, + is_headline=True, + is_opinion=True, + is_paid_content=False, + parent_url="wsj.com/politics,wsj.com/tech", + all_links="https://aiindex.stanford.edu/report,https://www.stateof.ai", + all_domain_links="who.int,nih.gov", + all_links_text="Nvidia,Tesla", + word_count_min=300, + word_count_max=1000, + page=2, + page_size=50, include_translation_fields=True, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", - ner_name="Tesla", + theme="Finance,Tech", + not_theme="Crime,Sports", + ner_name="Tesla,Amazon", + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, iptc_tags="20000199,20000209", not_iptc_tags="20000205,20000209", iab_tags="Business,Events", not_iab_tags="Agriculture,Metals", - custom_tags="Tag1,Tag2,Tag3", + custom_tags="Tag1,Tag2", + robots_compliant=True, ) @@ -1120,6 +913,7 @@ async def main() -> None: parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, word_count_min=word_count_min, word_count_max=word_count_max, page=page, @@ -1170,6 +964,7 @@ async def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, word_count_min: typing.Optional[WordCountMin] = OMIT, word_count_max: typing.Optional[WordCountMax] = OMIT, page: typing.Optional[Page] = OMIT, @@ -1243,6 +1038,8 @@ async def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + word_count_min : typing.Optional[WordCountMin] word_count_max : typing.Optional[WordCountMax] @@ -1294,7 +1091,6 @@ async def post( Examples -------- import asyncio - import datetime from newscatcher import AsyncNewscatcherApi @@ -1305,15 +1101,7 @@ async def post( async def main() -> None: await client.authors.post( - author_name="Joanna Stern", - sources=["wsj.com", "nytimes.com"], - lang="en", - from_=datetime.datetime.fromisoformat( - "2024-01-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-06-30 00:00:00+00:00", - ), + author_name="David Muir", ) @@ -1343,6 +1131,7 @@ async def main() -> None: parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, word_count_min=word_count_min, word_count_max=word_count_max, page=page, diff --git a/src/newscatcher/authors/raw_client.py b/src/newscatcher/authors/raw_client.py index 6f244c2..714aaec 100644 --- a/src/newscatcher/authors/raw_client.py +++ b/src/newscatcher/authors/raw_client.py @@ -1,13 +1,12 @@ # This file was auto-generated by Fern from our API Definition. -import datetime as dt import typing from json.decoder import JSONDecodeError from ..core.api_error import ApiError from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.datetime_utils import serialize_datetime from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.parse_error import ParsingError from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..core.serialization import convert_and_respect_annotation_metadata @@ -20,6 +19,7 @@ from ..errors.unprocessable_entity_error import UnprocessableEntityError from ..types.all_domain_links import AllDomainLinks from ..types.all_links import AllLinks +from ..types.all_links_text import AllLinksText from ..types.author_name import AuthorName from ..types.by_parse_date import ByParseDate from ..types.content_sentiment_max import ContentSentimentMax @@ -62,10 +62,9 @@ from ..types.to_rank import ToRank from ..types.word_count_max import WordCountMax from ..types.word_count_min import WordCountMin -from .types.authors_get_request_published_date_precision import AuthorsGetRequestPublishedDatePrecision -from .types.authors_get_request_sort_by import AuthorsGetRequestSortBy from .types.authors_get_response import AuthorsGetResponse from .types.authors_post_response import AuthorsPostResponse +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -78,7 +77,7 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): def get( self, *, - author_name: str, + author_name: AuthorName, not_author_name: typing.Optional[str] = None, predefined_sources: typing.Optional[str] = None, sources: typing.Optional[str] = None, @@ -87,40 +86,41 @@ def get( not_lang: typing.Optional[str] = None, countries: typing.Optional[str] = None, not_countries: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - published_date_precision: typing.Optional[AuthorsGetRequestPublishedDatePrecision] = None, - by_parse_date: typing.Optional[bool] = None, - sort_by: typing.Optional[AuthorsGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + from_: typing.Optional[From] = None, + to: typing.Optional[To] = None, + published_date_precision: typing.Optional[PublishedDatePrecision] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + all_links_text: typing.Optional[str] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - ner_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + ner_name: typing.Optional[NerName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, iab_tags: typing.Optional[str] = None, not_iab_tags: typing.Optional[str] = None, custom_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[AuthorsGetResponse]: """ @@ -128,154 +128,93 @@ def get( Parameters ---------- - author_name : str - The name of the author to search for. This parameter returns exact matches only. + author_name : AuthorName not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` + from_ : typing.Optional[From] - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. + to : typing.Optional[To] - published_date_precision : typing.Optional[AuthorsGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. + published_date_precision : typing.Optional[PublishedDatePrecision] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + by_parse_date : typing.Optional[ByParseDate] - sort_by : typing.Optional[AuthorsGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. + sort_by : typing.Optional[SortBy] - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + word_count_min : typing.Optional[WordCountMin] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + word_count_max : typing.Optional[WordCountMax] + + page : typing.Optional[Page] - page_size : typing.Optional[int] - The number of articles to return per page. + page_size : typing.Optional[PageSize] include_translation_fields : typing.Optional[IncludeTranslationFields] @@ -283,84 +222,23 @@ def get( has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - ner_name : typing.Optional[str] - The name of person, organization, location, product or other named entity to search for. To specify multiple names use a comma-separated string. - - Example: `"Tesla, Amazon"` - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + theme : typing.Optional[Theme] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + not_theme : typing.Optional[NotTheme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + ner_name : typing.Optional[NerName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -368,8 +246,6 @@ def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -377,8 +253,6 @@ def get( iab_tags : typing.Optional[str] Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - Example: `"Business, Events"` - **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -386,8 +260,6 @@ def get( not_iab_tags : typing.Optional[str] Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - Example: `"Agriculture, Metals"` - **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -395,14 +267,13 @@ def get( custom_tags : typing.Optional[str] Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. + - `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` + Example: `custom_tags.industry="Manufacturing,Logistics"` - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). + To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -425,8 +296,8 @@ def get( "not_lang": not_lang, "countries": countries, "not_countries": not_countries, - "from_": serialize_datetime(from_) if from_ is not None else None, - "to_": serialize_datetime(to) if to is not None else None, + "from_": convert_and_respect_annotation_metadata(object_=from_, annotation=From, direction="write"), + "to_": convert_and_respect_annotation_metadata(object_=to, annotation=To, direction="write"), "published_date_precision": published_date_precision, "by_parse_date": by_parse_date, "sort_by": sort_by, @@ -439,6 +310,7 @@ def get( "parent_url": parent_url, "all_links": all_links, "all_domain_links": all_domain_links, + "all_links_text": all_links_text, "word_count_min": word_count_min, "word_count_max": word_count_max, "page": page, @@ -552,6 +424,10 @@ def get( _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 post( @@ -580,6 +456,7 @@ def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, word_count_min: typing.Optional[WordCountMin] = OMIT, word_count_max: typing.Optional[WordCountMax] = OMIT, page: typing.Optional[Page] = OMIT, @@ -653,6 +530,8 @@ def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + word_count_min : typing.Optional[WordCountMin] word_count_max : typing.Optional[WordCountMax] @@ -748,6 +627,9 @@ def post( "all_domain_links": convert_and_respect_annotation_metadata( object_=all_domain_links, annotation=AllDomainLinks, direction="write" ), + "all_links_text": convert_and_respect_annotation_metadata( + object_=all_links_text, annotation=AllLinksText, direction="write" + ), "word_count_min": word_count_min, "word_count_max": word_count_max, "page": page, @@ -755,10 +637,8 @@ def post( "include_translation_fields": include_translation_fields, "include_nlp_data": include_nlp_data, "has_nlp": has_nlp, - "theme": convert_and_respect_annotation_metadata(object_=theme, annotation=Theme, direction="write"), - "not_theme": convert_and_respect_annotation_metadata( - object_=not_theme, annotation=NotTheme, direction="write" - ), + "theme": theme, + "not_theme": not_theme, "ner_name": ner_name, "title_sentiment_min": title_sentiment_min, "title_sentiment_max": title_sentiment_max, @@ -877,6 +757,10 @@ def post( _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) @@ -887,7 +771,7 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): async def get( self, *, - author_name: str, + author_name: AuthorName, not_author_name: typing.Optional[str] = None, predefined_sources: typing.Optional[str] = None, sources: typing.Optional[str] = None, @@ -896,40 +780,41 @@ async def get( not_lang: typing.Optional[str] = None, countries: typing.Optional[str] = None, not_countries: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - published_date_precision: typing.Optional[AuthorsGetRequestPublishedDatePrecision] = None, - by_parse_date: typing.Optional[bool] = None, - sort_by: typing.Optional[AuthorsGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + from_: typing.Optional[From] = None, + to: typing.Optional[To] = None, + published_date_precision: typing.Optional[PublishedDatePrecision] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + all_links_text: typing.Optional[str] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - ner_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + ner_name: typing.Optional[NerName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, iab_tags: typing.Optional[str] = None, not_iab_tags: typing.Optional[str] = None, custom_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[AuthorsGetResponse]: """ @@ -937,154 +822,93 @@ async def get( Parameters ---------- - author_name : str - The name of the author to search for. This parameter returns exact matches only. + author_name : AuthorName not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` + from_ : typing.Optional[From] - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. + to : typing.Optional[To] - published_date_precision : typing.Optional[AuthorsGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. + published_date_precision : typing.Optional[PublishedDatePrecision] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + by_parse_date : typing.Optional[ByParseDate] - sort_by : typing.Optional[AuthorsGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. + sort_by : typing.Optional[SortBy] - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + word_count_min : typing.Optional[WordCountMin] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + word_count_max : typing.Optional[WordCountMax] + + page : typing.Optional[Page] - page_size : typing.Optional[int] - The number of articles to return per page. + page_size : typing.Optional[PageSize] include_translation_fields : typing.Optional[IncludeTranslationFields] @@ -1092,84 +916,23 @@ async def get( has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - ner_name : typing.Optional[str] - The name of person, organization, location, product or other named entity to search for. To specify multiple names use a comma-separated string. - - Example: `"Tesla, Amazon"` - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + theme : typing.Optional[Theme] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + not_theme : typing.Optional[NotTheme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + ner_name : typing.Optional[NerName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1177,8 +940,6 @@ async def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1186,8 +947,6 @@ async def get( iab_tags : typing.Optional[str] Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - Example: `"Business, Events"` - **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -1195,8 +954,6 @@ async def get( not_iab_tags : typing.Optional[str] Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - Example: `"Agriculture, Metals"` - **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -1204,14 +961,13 @@ async def get( custom_tags : typing.Optional[str] Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. + - `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` + Example: `custom_tags.industry="Manufacturing,Logistics"` - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). + To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1234,8 +990,8 @@ async def get( "not_lang": not_lang, "countries": countries, "not_countries": not_countries, - "from_": serialize_datetime(from_) if from_ is not None else None, - "to_": serialize_datetime(to) if to is not None else None, + "from_": convert_and_respect_annotation_metadata(object_=from_, annotation=From, direction="write"), + "to_": convert_and_respect_annotation_metadata(object_=to, annotation=To, direction="write"), "published_date_precision": published_date_precision, "by_parse_date": by_parse_date, "sort_by": sort_by, @@ -1248,6 +1004,7 @@ async def get( "parent_url": parent_url, "all_links": all_links, "all_domain_links": all_domain_links, + "all_links_text": all_links_text, "word_count_min": word_count_min, "word_count_max": word_count_max, "page": page, @@ -1361,6 +1118,10 @@ async def get( _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 post( @@ -1389,6 +1150,7 @@ async def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, word_count_min: typing.Optional[WordCountMin] = OMIT, word_count_max: typing.Optional[WordCountMax] = OMIT, page: typing.Optional[Page] = OMIT, @@ -1462,6 +1224,8 @@ async def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + word_count_min : typing.Optional[WordCountMin] word_count_max : typing.Optional[WordCountMax] @@ -1557,6 +1321,9 @@ async def post( "all_domain_links": convert_and_respect_annotation_metadata( object_=all_domain_links, annotation=AllDomainLinks, direction="write" ), + "all_links_text": convert_and_respect_annotation_metadata( + object_=all_links_text, annotation=AllLinksText, direction="write" + ), "word_count_min": word_count_min, "word_count_max": word_count_max, "page": page, @@ -1564,10 +1331,8 @@ async def post( "include_translation_fields": include_translation_fields, "include_nlp_data": include_nlp_data, "has_nlp": has_nlp, - "theme": convert_and_respect_annotation_metadata(object_=theme, annotation=Theme, direction="write"), - "not_theme": convert_and_respect_annotation_metadata( - object_=not_theme, annotation=NotTheme, direction="write" - ), + "theme": theme, + "not_theme": not_theme, "ner_name": ner_name, "title_sentiment_min": title_sentiment_min, "title_sentiment_max": title_sentiment_max, @@ -1686,4 +1451,8 @@ async def post( _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) diff --git a/src/newscatcher/authors/types/__init__.py b/src/newscatcher/authors/types/__init__.py index afa44b2..3766d81 100644 --- a/src/newscatcher/authors/types/__init__.py +++ b/src/newscatcher/authors/types/__init__.py @@ -2,14 +2,37 @@ # isort: skip_file -from .authors_get_request_published_date_precision import AuthorsGetRequestPublishedDatePrecision -from .authors_get_request_sort_by import AuthorsGetRequestSortBy -from .authors_get_response import AuthorsGetResponse -from .authors_post_response import AuthorsPostResponse - -__all__ = [ - "AuthorsGetRequestPublishedDatePrecision", - "AuthorsGetRequestSortBy", - "AuthorsGetResponse", - "AuthorsPostResponse", -] +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .authors_get_response import AuthorsGetResponse + from .authors_post_response import AuthorsPostResponse +_dynamic_imports: typing.Dict[str, str] = { + "AuthorsGetResponse": ".authors_get_response", + "AuthorsPostResponse": ".authors_post_response", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["AuthorsGetResponse", "AuthorsPostResponse"] diff --git a/src/newscatcher/authors/types/authors_get_request_published_date_precision.py b/src/newscatcher/authors/types/authors_get_request_published_date_precision.py deleted file mode 100644 index 8999022..0000000 --- a/src/newscatcher/authors/types/authors_get_request_published_date_precision.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -AuthorsGetRequestPublishedDatePrecision = typing.Union[typing.Literal["full", "timezone unknown", "date"], typing.Any] diff --git a/src/newscatcher/authors/types/authors_get_request_sort_by.py b/src/newscatcher/authors/types/authors_get_request_sort_by.py deleted file mode 100644 index 62b8da7..0000000 --- a/src/newscatcher/authors/types/authors_get_request_sort_by.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -AuthorsGetRequestSortBy = typing.Union[typing.Literal["relevancy", "date", "rank"], typing.Any] diff --git a/src/newscatcher/base_client.py b/src/newscatcher/base_client.py index 04a1f90..4869cb1 100644 --- a/src/newscatcher/base_client.py +++ b/src/newscatcher/base_client.py @@ -1,19 +1,23 @@ # This file was auto-generated by Fern from our API Definition. +from __future__ import annotations + import typing import httpx -from .aggregation.client import AggregationClient, AsyncAggregationClient -from .authors.client import AsyncAuthorsClient, AuthorsClient -from .breaking_news.client import AsyncBreakingNewsClient, BreakingNewsClient from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from .core.logging import LogConfig, Logger from .environment import NewscatcherApiEnvironment -from .latestheadlines.client import AsyncLatestheadlinesClient, LatestheadlinesClient -from .search.client import AsyncSearchClient, SearchClient -from .search_link.client import AsyncSearchLinkClient, SearchLinkClient -from .searchsimilar.client import AsyncSearchsimilarClient, SearchsimilarClient -from .sources.client import AsyncSourcesClient, SourcesClient -from .subscription.client import AsyncSubscriptionClient, SubscriptionClient + +if typing.TYPE_CHECKING: + from .aggregation.client import AggregationClient, AsyncAggregationClient + from .authors.client import AsyncAuthorsClient, AuthorsClient + from .breaking_news.client import AsyncBreakingNewsClient, BreakingNewsClient + from .latest_headlines.client import AsyncLatestHeadlinesClient, LatestHeadlinesClient + from .search.client import AsyncSearchClient, SearchClient + from .search_by_link.client import AsyncSearchByLinkClient, SearchByLinkClient + from .sources.client import AsyncSourcesClient, SourcesClient + from .subscription.client import AsyncSubscriptionClient, SubscriptionClient class BaseNewscatcherApi: @@ -47,6 +51,9 @@ class BaseNewscatcherApi: httpx_client : typing.Optional[httpx.Client] The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + logging : typing.Optional[typing.Union[LogConfig, Logger]] + Configure logging for the SDK. Accepts a LogConfig dict with 'level' (debug/info/warn/error), 'logger' (custom logger implementation), and 'silent' (boolean, defaults to True) fields. You can also pass a pre-configured Logger instance. + Examples -------- from newscatcher import NewscatcherApi @@ -66,6 +73,7 @@ def __init__( timeout: typing.Optional[float] = None, follow_redirects: typing.Optional[bool] = True, httpx_client: typing.Optional[httpx.Client] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): _defaulted_timeout = ( timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read @@ -80,16 +88,80 @@ def __init__( if follow_redirects is not None else httpx.Client(timeout=_defaulted_timeout), timeout=_defaulted_timeout, + logging=logging, ) - self.search = SearchClient(client_wrapper=self._client_wrapper) - self.latestheadlines = LatestheadlinesClient(client_wrapper=self._client_wrapper) - self.breaking_news = BreakingNewsClient(client_wrapper=self._client_wrapper) - self.authors = AuthorsClient(client_wrapper=self._client_wrapper) - self.search_link = SearchLinkClient(client_wrapper=self._client_wrapper) - self.searchsimilar = SearchsimilarClient(client_wrapper=self._client_wrapper) - self.sources = SourcesClient(client_wrapper=self._client_wrapper) - self.aggregation = AggregationClient(client_wrapper=self._client_wrapper) - self.subscription = SubscriptionClient(client_wrapper=self._client_wrapper) + self._search: typing.Optional[SearchClient] = None + self._latest_headlines: typing.Optional[LatestHeadlinesClient] = None + self._breaking_news: typing.Optional[BreakingNewsClient] = None + self._authors: typing.Optional[AuthorsClient] = None + self._search_by_link: typing.Optional[SearchByLinkClient] = None + self._sources: typing.Optional[SourcesClient] = None + self._aggregation: typing.Optional[AggregationClient] = None + self._subscription: typing.Optional[SubscriptionClient] = None + + @property + def search(self): + if self._search is None: + from .search.client import SearchClient # noqa: E402 + + self._search = SearchClient(client_wrapper=self._client_wrapper) + return self._search + + @property + def latest_headlines(self): + if self._latest_headlines is None: + from .latest_headlines.client import LatestHeadlinesClient # noqa: E402 + + self._latest_headlines = LatestHeadlinesClient(client_wrapper=self._client_wrapper) + return self._latest_headlines + + @property + def breaking_news(self): + if self._breaking_news is None: + from .breaking_news.client import BreakingNewsClient # noqa: E402 + + self._breaking_news = BreakingNewsClient(client_wrapper=self._client_wrapper) + return self._breaking_news + + @property + def authors(self): + if self._authors is None: + from .authors.client import AuthorsClient # noqa: E402 + + self._authors = AuthorsClient(client_wrapper=self._client_wrapper) + return self._authors + + @property + def search_by_link(self): + if self._search_by_link is None: + from .search_by_link.client import SearchByLinkClient # noqa: E402 + + self._search_by_link = SearchByLinkClient(client_wrapper=self._client_wrapper) + return self._search_by_link + + @property + def sources(self): + if self._sources is None: + from .sources.client import SourcesClient # noqa: E402 + + self._sources = SourcesClient(client_wrapper=self._client_wrapper) + return self._sources + + @property + def aggregation(self): + if self._aggregation is None: + from .aggregation.client import AggregationClient # noqa: E402 + + self._aggregation = AggregationClient(client_wrapper=self._client_wrapper) + return self._aggregation + + @property + def subscription(self): + if self._subscription is None: + from .subscription.client import SubscriptionClient # noqa: E402 + + self._subscription = SubscriptionClient(client_wrapper=self._client_wrapper) + return self._subscription class AsyncBaseNewscatcherApi: @@ -123,6 +195,9 @@ class AsyncBaseNewscatcherApi: httpx_client : typing.Optional[httpx.AsyncClient] The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + logging : typing.Optional[typing.Union[LogConfig, Logger]] + Configure logging for the SDK. Accepts a LogConfig dict with 'level' (debug/info/warn/error), 'logger' (custom logger implementation), and 'silent' (boolean, defaults to True) fields. You can also pass a pre-configured Logger instance. + Examples -------- from newscatcher import AsyncNewscatcherApi @@ -142,6 +217,7 @@ def __init__( timeout: typing.Optional[float] = None, follow_redirects: typing.Optional[bool] = True, httpx_client: typing.Optional[httpx.AsyncClient] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): _defaulted_timeout = ( timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read @@ -156,16 +232,80 @@ def __init__( if follow_redirects is not None else httpx.AsyncClient(timeout=_defaulted_timeout), timeout=_defaulted_timeout, + logging=logging, ) - self.search = AsyncSearchClient(client_wrapper=self._client_wrapper) - self.latestheadlines = AsyncLatestheadlinesClient(client_wrapper=self._client_wrapper) - self.breaking_news = AsyncBreakingNewsClient(client_wrapper=self._client_wrapper) - self.authors = AsyncAuthorsClient(client_wrapper=self._client_wrapper) - self.search_link = AsyncSearchLinkClient(client_wrapper=self._client_wrapper) - self.searchsimilar = AsyncSearchsimilarClient(client_wrapper=self._client_wrapper) - self.sources = AsyncSourcesClient(client_wrapper=self._client_wrapper) - self.aggregation = AsyncAggregationClient(client_wrapper=self._client_wrapper) - self.subscription = AsyncSubscriptionClient(client_wrapper=self._client_wrapper) + self._search: typing.Optional[AsyncSearchClient] = None + self._latest_headlines: typing.Optional[AsyncLatestHeadlinesClient] = None + self._breaking_news: typing.Optional[AsyncBreakingNewsClient] = None + self._authors: typing.Optional[AsyncAuthorsClient] = None + self._search_by_link: typing.Optional[AsyncSearchByLinkClient] = None + self._sources: typing.Optional[AsyncSourcesClient] = None + self._aggregation: typing.Optional[AsyncAggregationClient] = None + self._subscription: typing.Optional[AsyncSubscriptionClient] = None + + @property + def search(self): + if self._search is None: + from .search.client import AsyncSearchClient # noqa: E402 + + self._search = AsyncSearchClient(client_wrapper=self._client_wrapper) + return self._search + + @property + def latest_headlines(self): + if self._latest_headlines is None: + from .latest_headlines.client import AsyncLatestHeadlinesClient # noqa: E402 + + self._latest_headlines = AsyncLatestHeadlinesClient(client_wrapper=self._client_wrapper) + return self._latest_headlines + + @property + def breaking_news(self): + if self._breaking_news is None: + from .breaking_news.client import AsyncBreakingNewsClient # noqa: E402 + + self._breaking_news = AsyncBreakingNewsClient(client_wrapper=self._client_wrapper) + return self._breaking_news + + @property + def authors(self): + if self._authors is None: + from .authors.client import AsyncAuthorsClient # noqa: E402 + + self._authors = AsyncAuthorsClient(client_wrapper=self._client_wrapper) + return self._authors + + @property + def search_by_link(self): + if self._search_by_link is None: + from .search_by_link.client import AsyncSearchByLinkClient # noqa: E402 + + self._search_by_link = AsyncSearchByLinkClient(client_wrapper=self._client_wrapper) + return self._search_by_link + + @property + def sources(self): + if self._sources is None: + from .sources.client import AsyncSourcesClient # noqa: E402 + + self._sources = AsyncSourcesClient(client_wrapper=self._client_wrapper) + return self._sources + + @property + def aggregation(self): + if self._aggregation is None: + from .aggregation.client import AsyncAggregationClient # noqa: E402 + + self._aggregation = AsyncAggregationClient(client_wrapper=self._client_wrapper) + return self._aggregation + + @property + def subscription(self): + if self._subscription is None: + from .subscription.client import AsyncSubscriptionClient # noqa: E402 + + self._subscription = AsyncSubscriptionClient(client_wrapper=self._client_wrapper) + return self._subscription def _get_base_url(*, base_url: typing.Optional[str] = None, environment: NewscatcherApiEnvironment) -> str: diff --git a/src/newscatcher/breaking_news/__init__.py b/src/newscatcher/breaking_news/__init__.py index 8661612..5cde020 100644 --- a/src/newscatcher/breaking_news/__init__.py +++ b/src/newscatcher/breaking_news/__init__.py @@ -2,6 +2,3 @@ # isort: skip_file -from .types import BreakingNewsGetRequestSortBy - -__all__ = ["BreakingNewsGetRequestSortBy"] diff --git a/src/newscatcher/breaking_news/client.py b/src/newscatcher/breaking_news/client.py index 350a29a..ac47861 100644 --- a/src/newscatcher/breaking_news/client.py +++ b/src/newscatcher/breaking_news/client.py @@ -19,7 +19,6 @@ from ..types.page_size import PageSize from ..types.per_entity_name import PerEntityName from ..types.ranked_only import RankedOnly -from ..types.robots_compliant import RobotsCompliant from ..types.sort_by import SortBy from ..types.theme import Theme from ..types.title_sentiment_max import TitleSentimentMax @@ -27,7 +26,6 @@ from ..types.to_rank import ToRank from ..types.top_n_articles import TopNArticles from .raw_client import AsyncRawBreakingNewsClient, RawBreakingNewsClient -from .types.breaking_news_get_request_sort_by import BreakingNewsGetRequestSortBy # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -51,27 +49,26 @@ def with_raw_response(self) -> RawBreakingNewsClient: def breaking_news_get( self, *, - sort_by: typing.Optional[BreakingNewsGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, top_n_articles: typing.Optional[TopNArticles] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, - robots_compliant: typing.Optional[bool] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, request_options: typing.Optional[RequestOptions] = None, ) -> BreakingNewsResponseDto: """ @@ -79,28 +76,17 @@ def breaking_news_get( Parameters ---------- - sort_by : typing.Optional[BreakingNewsGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. - - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + sort_by : typing.Optional[SortBy] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + ranked_only : typing.Optional[RankedOnly] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + to_rank : typing.Optional[ToRank] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + page : typing.Optional[Page] - page_size : typing.Optional[int] - The number of articles to return per page. + page_size : typing.Optional[PageSize] top_n_articles : typing.Optional[TopNArticles] @@ -110,112 +96,25 @@ def breaking_news_get( has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + theme : typing.Optional[Theme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + not_theme : typing.Optional[NotTheme] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + org_entity_name : typing.Optional[OrgEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + per_entity_name : typing.Optional[PerEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + loc_entity_name : typing.Optional[LocEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + misc_entity_name : typing.Optional[MiscEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_min : typing.Optional[TitleSentimentMin] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + title_sentiment_max : typing.Optional[TitleSentimentMax] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_min : typing.Optional[ContentSentimentMin] - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + content_sentiment_max : typing.Optional[ContentSentimentMax] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -233,12 +132,25 @@ def breaking_news_get( api_key="YOUR_API_KEY", ) client.breaking_news.breaking_news_get( + ranked_only=True, + from_rank=100, + to_rank=100, + page=2, + page_size=50, top_n_articles=5, include_translation_fields=True, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", + theme="Finance,Tech", + not_theme="Crime,Sports", + org_entity_name='"Apple Inc" OR Microsoft', + per_entity_name='"Elon Musk" OR "Jeff Bezos"', + loc_entity_name='"San Francisco" OR "New York City"', + misc_entity_name='AWS OR "Microsoft Azure"', + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, ) """ _response = self._raw_client.breaking_news_get( @@ -262,7 +174,6 @@ def breaking_news_get( title_sentiment_max=title_sentiment_max, content_sentiment_min=content_sentiment_min, content_sentiment_max=content_sentiment_max, - robots_compliant=robots_compliant, request_options=request_options, ) return _response.data @@ -289,8 +200,7 @@ def breaking_news_post( title_sentiment_min: typing.Optional[TitleSentimentMin] = OMIT, title_sentiment_max: typing.Optional[TitleSentimentMax] = OMIT, content_sentiment_min: typing.Optional[ContentSentimentMin] = OMIT, - content_sentient_max: typing.Optional[ContentSentimentMax] = OMIT, - robots_compliant: typing.Optional[RobotsCompliant] = OMIT, + content_sentiment_max: typing.Optional[ContentSentimentMax] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> BreakingNewsResponseDto: """ @@ -336,9 +246,7 @@ def breaking_news_post( content_sentiment_min : typing.Optional[ContentSentimentMin] - content_sentient_max : typing.Optional[ContentSentimentMax] - - robots_compliant : typing.Optional[RobotsCompliant] + content_sentiment_max : typing.Optional[ContentSentimentMax] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -357,9 +265,8 @@ def breaking_news_post( ) client.breaking_news.breaking_news_post( sort_by="relevancy", - page=1, - page_size=100, - include_nlp_data=True, + ranked_only=True, + top_n_articles=1, ) """ _response = self._raw_client.breaking_news_post( @@ -382,8 +289,7 @@ def breaking_news_post( title_sentiment_min=title_sentiment_min, title_sentiment_max=title_sentiment_max, content_sentiment_min=content_sentiment_min, - content_sentient_max=content_sentient_max, - robots_compliant=robots_compliant, + content_sentiment_max=content_sentiment_max, request_options=request_options, ) return _response.data @@ -407,27 +313,26 @@ def with_raw_response(self) -> AsyncRawBreakingNewsClient: async def breaking_news_get( self, *, - sort_by: typing.Optional[BreakingNewsGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, top_n_articles: typing.Optional[TopNArticles] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, - robots_compliant: typing.Optional[bool] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, request_options: typing.Optional[RequestOptions] = None, ) -> BreakingNewsResponseDto: """ @@ -435,28 +340,17 @@ async def breaking_news_get( Parameters ---------- - sort_by : typing.Optional[BreakingNewsGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. - - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + sort_by : typing.Optional[SortBy] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + ranked_only : typing.Optional[RankedOnly] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + to_rank : typing.Optional[ToRank] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + page : typing.Optional[Page] - page_size : typing.Optional[int] - The number of articles to return per page. + page_size : typing.Optional[PageSize] top_n_articles : typing.Optional[TopNArticles] @@ -466,112 +360,25 @@ async def breaking_news_get( has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + theme : typing.Optional[Theme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + not_theme : typing.Optional[NotTheme] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + org_entity_name : typing.Optional[OrgEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + per_entity_name : typing.Optional[PerEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + loc_entity_name : typing.Optional[LocEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + misc_entity_name : typing.Optional[MiscEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_min : typing.Optional[TitleSentimentMin] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + title_sentiment_max : typing.Optional[TitleSentimentMax] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_min : typing.Optional[ContentSentimentMin] - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + content_sentiment_max : typing.Optional[ContentSentimentMax] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -594,12 +401,25 @@ async def breaking_news_get( async def main() -> None: await client.breaking_news.breaking_news_get( + ranked_only=True, + from_rank=100, + to_rank=100, + page=2, + page_size=50, top_n_articles=5, include_translation_fields=True, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", + theme="Finance,Tech", + not_theme="Crime,Sports", + org_entity_name='"Apple Inc" OR Microsoft', + per_entity_name='"Elon Musk" OR "Jeff Bezos"', + loc_entity_name='"San Francisco" OR "New York City"', + misc_entity_name='AWS OR "Microsoft Azure"', + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, ) @@ -626,7 +446,6 @@ async def main() -> None: title_sentiment_max=title_sentiment_max, content_sentiment_min=content_sentiment_min, content_sentiment_max=content_sentiment_max, - robots_compliant=robots_compliant, request_options=request_options, ) return _response.data @@ -653,8 +472,7 @@ async def breaking_news_post( title_sentiment_min: typing.Optional[TitleSentimentMin] = OMIT, title_sentiment_max: typing.Optional[TitleSentimentMax] = OMIT, content_sentiment_min: typing.Optional[ContentSentimentMin] = OMIT, - content_sentient_max: typing.Optional[ContentSentimentMax] = OMIT, - robots_compliant: typing.Optional[RobotsCompliant] = OMIT, + content_sentiment_max: typing.Optional[ContentSentimentMax] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> BreakingNewsResponseDto: """ @@ -700,9 +518,7 @@ async def breaking_news_post( content_sentiment_min : typing.Optional[ContentSentimentMin] - content_sentient_max : typing.Optional[ContentSentimentMax] - - robots_compliant : typing.Optional[RobotsCompliant] + content_sentiment_max : typing.Optional[ContentSentimentMax] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -726,9 +542,8 @@ async def breaking_news_post( async def main() -> None: await client.breaking_news.breaking_news_post( sort_by="relevancy", - page=1, - page_size=100, - include_nlp_data=True, + ranked_only=True, + top_n_articles=1, ) @@ -754,8 +569,7 @@ async def main() -> None: title_sentiment_min=title_sentiment_min, title_sentiment_max=title_sentiment_max, content_sentiment_min=content_sentiment_min, - content_sentient_max=content_sentient_max, - robots_compliant=robots_compliant, + content_sentiment_max=content_sentiment_max, request_options=request_options, ) return _response.data diff --git a/src/newscatcher/breaking_news/raw_client.py b/src/newscatcher/breaking_news/raw_client.py index be4c042..69b177f 100644 --- a/src/newscatcher/breaking_news/raw_client.py +++ b/src/newscatcher/breaking_news/raw_client.py @@ -6,9 +6,9 @@ from ..core.api_error import ApiError from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.parse_error import ParsingError from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions -from ..core.serialization import convert_and_respect_annotation_metadata from ..errors.bad_request_error import BadRequestError from ..errors.forbidden_error import ForbiddenError from ..errors.internal_server_error import InternalServerError @@ -32,14 +32,13 @@ from ..types.page_size import PageSize from ..types.per_entity_name import PerEntityName from ..types.ranked_only import RankedOnly -from ..types.robots_compliant import RobotsCompliant from ..types.sort_by import SortBy from ..types.theme import Theme from ..types.title_sentiment_max import TitleSentimentMax from ..types.title_sentiment_min import TitleSentimentMin from ..types.to_rank import ToRank from ..types.top_n_articles import TopNArticles -from .types.breaking_news_get_request_sort_by import BreakingNewsGetRequestSortBy +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -52,27 +51,26 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): def breaking_news_get( self, *, - sort_by: typing.Optional[BreakingNewsGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, top_n_articles: typing.Optional[TopNArticles] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, - robots_compliant: typing.Optional[bool] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[BreakingNewsResponseDto]: """ @@ -80,28 +78,17 @@ def breaking_news_get( Parameters ---------- - sort_by : typing.Optional[BreakingNewsGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. - - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + sort_by : typing.Optional[SortBy] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + ranked_only : typing.Optional[RankedOnly] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + to_rank : typing.Optional[ToRank] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + page : typing.Optional[Page] - page_size : typing.Optional[int] - The number of articles to return per page. + page_size : typing.Optional[PageSize] top_n_articles : typing.Optional[TopNArticles] @@ -111,112 +98,25 @@ def breaking_news_get( has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + theme : typing.Optional[Theme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + not_theme : typing.Optional[NotTheme] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + org_entity_name : typing.Optional[OrgEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + per_entity_name : typing.Optional[PerEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + loc_entity_name : typing.Optional[LocEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + misc_entity_name : typing.Optional[MiscEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_min : typing.Optional[TitleSentimentMin] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + title_sentiment_max : typing.Optional[TitleSentimentMax] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_min : typing.Optional[ContentSentimentMin] - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + content_sentiment_max : typing.Optional[ContentSentimentMax] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -250,7 +150,6 @@ def breaking_news_get( "title_sentiment_max": title_sentiment_max, "content_sentiment_min": content_sentiment_min, "content_sentiment_max": content_sentiment_max, - "robots_compliant": robots_compliant, }, request_options=request_options, ) @@ -344,6 +243,10 @@ def breaking_news_get( _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 breaking_news_post( @@ -368,8 +271,7 @@ def breaking_news_post( title_sentiment_min: typing.Optional[TitleSentimentMin] = OMIT, title_sentiment_max: typing.Optional[TitleSentimentMax] = OMIT, content_sentiment_min: typing.Optional[ContentSentimentMin] = OMIT, - content_sentient_max: typing.Optional[ContentSentimentMax] = OMIT, - robots_compliant: typing.Optional[RobotsCompliant] = OMIT, + content_sentiment_max: typing.Optional[ContentSentimentMax] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[BreakingNewsResponseDto]: """ @@ -415,9 +317,7 @@ def breaking_news_post( content_sentiment_min : typing.Optional[ContentSentimentMin] - content_sentient_max : typing.Optional[ContentSentimentMax] - - robots_compliant : typing.Optional[RobotsCompliant] + content_sentiment_max : typing.Optional[ContentSentimentMax] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -441,10 +341,8 @@ def breaking_news_post( "include_translation_fields": include_translation_fields, "include_nlp_data": include_nlp_data, "has_nlp": has_nlp, - "theme": convert_and_respect_annotation_metadata(object_=theme, annotation=Theme, direction="write"), - "not_theme": convert_and_respect_annotation_metadata( - object_=not_theme, annotation=NotTheme, direction="write" - ), + "theme": theme, + "not_theme": not_theme, "ORG_entity_name": org_entity_name, "PER_entity_name": per_entity_name, "LOC_entity_name": loc_entity_name, @@ -452,8 +350,7 @@ def breaking_news_post( "title_sentiment_min": title_sentiment_min, "title_sentiment_max": title_sentiment_max, "content_sentiment_min": content_sentiment_min, - "content_sentient_max": content_sentient_max, - "robots_compliant": robots_compliant, + "content_sentiment_max": content_sentiment_max, }, headers={ "content-type": "application/json", @@ -551,6 +448,10 @@ def breaking_news_post( _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) @@ -561,27 +462,26 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): async def breaking_news_get( self, *, - sort_by: typing.Optional[BreakingNewsGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, top_n_articles: typing.Optional[TopNArticles] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, - robots_compliant: typing.Optional[bool] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[BreakingNewsResponseDto]: """ @@ -589,28 +489,17 @@ async def breaking_news_get( Parameters ---------- - sort_by : typing.Optional[BreakingNewsGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. - - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + sort_by : typing.Optional[SortBy] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + ranked_only : typing.Optional[RankedOnly] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + to_rank : typing.Optional[ToRank] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + page : typing.Optional[Page] - page_size : typing.Optional[int] - The number of articles to return per page. + page_size : typing.Optional[PageSize] top_n_articles : typing.Optional[TopNArticles] @@ -620,112 +509,25 @@ async def breaking_news_get( has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + theme : typing.Optional[Theme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + not_theme : typing.Optional[NotTheme] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + org_entity_name : typing.Optional[OrgEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + per_entity_name : typing.Optional[PerEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + loc_entity_name : typing.Optional[LocEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + misc_entity_name : typing.Optional[MiscEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_min : typing.Optional[TitleSentimentMin] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + title_sentiment_max : typing.Optional[TitleSentimentMax] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_min : typing.Optional[ContentSentimentMin] - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + content_sentiment_max : typing.Optional[ContentSentimentMax] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -759,7 +561,6 @@ async def breaking_news_get( "title_sentiment_max": title_sentiment_max, "content_sentiment_min": content_sentiment_min, "content_sentiment_max": content_sentiment_max, - "robots_compliant": robots_compliant, }, request_options=request_options, ) @@ -853,6 +654,10 @@ async def breaking_news_get( _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 breaking_news_post( @@ -877,8 +682,7 @@ async def breaking_news_post( title_sentiment_min: typing.Optional[TitleSentimentMin] = OMIT, title_sentiment_max: typing.Optional[TitleSentimentMax] = OMIT, content_sentiment_min: typing.Optional[ContentSentimentMin] = OMIT, - content_sentient_max: typing.Optional[ContentSentimentMax] = OMIT, - robots_compliant: typing.Optional[RobotsCompliant] = OMIT, + content_sentiment_max: typing.Optional[ContentSentimentMax] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[BreakingNewsResponseDto]: """ @@ -924,9 +728,7 @@ async def breaking_news_post( content_sentiment_min : typing.Optional[ContentSentimentMin] - content_sentient_max : typing.Optional[ContentSentimentMax] - - robots_compliant : typing.Optional[RobotsCompliant] + content_sentiment_max : typing.Optional[ContentSentimentMax] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -950,10 +752,8 @@ async def breaking_news_post( "include_translation_fields": include_translation_fields, "include_nlp_data": include_nlp_data, "has_nlp": has_nlp, - "theme": convert_and_respect_annotation_metadata(object_=theme, annotation=Theme, direction="write"), - "not_theme": convert_and_respect_annotation_metadata( - object_=not_theme, annotation=NotTheme, direction="write" - ), + "theme": theme, + "not_theme": not_theme, "ORG_entity_name": org_entity_name, "PER_entity_name": per_entity_name, "LOC_entity_name": loc_entity_name, @@ -961,8 +761,7 @@ async def breaking_news_post( "title_sentiment_min": title_sentiment_min, "title_sentiment_max": title_sentiment_max, "content_sentiment_min": content_sentiment_min, - "content_sentient_max": content_sentient_max, - "robots_compliant": robots_compliant, + "content_sentiment_max": content_sentiment_max, }, headers={ "content-type": "application/json", @@ -1060,4 +859,8 @@ async def breaking_news_post( _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) diff --git a/src/newscatcher/breaking_news/types/__init__.py b/src/newscatcher/breaking_news/types/__init__.py deleted file mode 100644 index 7ad450d..0000000 --- a/src/newscatcher/breaking_news/types/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -from .breaking_news_get_request_sort_by import BreakingNewsGetRequestSortBy - -__all__ = ["BreakingNewsGetRequestSortBy"] diff --git a/src/newscatcher/breaking_news/types/breaking_news_get_request_sort_by.py b/src/newscatcher/breaking_news/types/breaking_news_get_request_sort_by.py deleted file mode 100644 index a806b7d..0000000 --- a/src/newscatcher/breaking_news/types/breaking_news_get_request_sort_by.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -BreakingNewsGetRequestSortBy = typing.Union[typing.Literal["relevancy", "date", "rank"], typing.Any] diff --git a/src/newscatcher/core/__init__.py b/src/newscatcher/core/__init__.py index 31bbb81..4fb6e12 100644 --- a/src/newscatcher/core/__init__.py +++ b/src/newscatcher/core/__init__.py @@ -2,26 +2,90 @@ # isort: skip_file -from .api_error import ApiError -from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper -from .datetime_utils import serialize_datetime -from .file import File, convert_file_dict_to_httpx_tuples, with_content_type -from .http_client import AsyncHttpClient, HttpClient -from .http_response import AsyncHttpResponse, HttpResponse -from .jsonable_encoder import jsonable_encoder -from .pydantic_utilities import ( - IS_PYDANTIC_V2, - UniversalBaseModel, - UniversalRootModel, - parse_obj_as, - universal_field_validator, - universal_root_validator, - update_forward_refs, -) -from .query_encoder import encode_query -from .remove_none_from_dict import remove_none_from_dict -from .request_options import RequestOptions -from .serialization import FieldMetadata, convert_and_respect_annotation_metadata +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .api_error import ApiError + from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper + from .datetime_utils import Rfc2822DateTime, parse_rfc2822_datetime, serialize_datetime + from .file import File, convert_file_dict_to_httpx_tuples, with_content_type + from .http_client import AsyncHttpClient, HttpClient + from .http_response import AsyncHttpResponse, HttpResponse + from .jsonable_encoder import jsonable_encoder + from .logging import ConsoleLogger, ILogger, LogConfig, LogLevel, Logger, create_logger + from .parse_error import ParsingError + from .pydantic_utilities import ( + IS_PYDANTIC_V2, + UniversalBaseModel, + UniversalRootModel, + parse_obj_as, + universal_field_validator, + universal_root_validator, + update_forward_refs, + ) + from .query_encoder import encode_query + from .remove_none_from_dict import remove_none_from_dict + from .request_options import RequestOptions + from .serialization import FieldMetadata, convert_and_respect_annotation_metadata +_dynamic_imports: typing.Dict[str, str] = { + "ApiError": ".api_error", + "AsyncClientWrapper": ".client_wrapper", + "AsyncHttpClient": ".http_client", + "AsyncHttpResponse": ".http_response", + "BaseClientWrapper": ".client_wrapper", + "ConsoleLogger": ".logging", + "FieldMetadata": ".serialization", + "File": ".file", + "HttpClient": ".http_client", + "HttpResponse": ".http_response", + "ILogger": ".logging", + "IS_PYDANTIC_V2": ".pydantic_utilities", + "LogConfig": ".logging", + "LogLevel": ".logging", + "Logger": ".logging", + "ParsingError": ".parse_error", + "RequestOptions": ".request_options", + "Rfc2822DateTime": ".datetime_utils", + "SyncClientWrapper": ".client_wrapper", + "UniversalBaseModel": ".pydantic_utilities", + "UniversalRootModel": ".pydantic_utilities", + "convert_and_respect_annotation_metadata": ".serialization", + "convert_file_dict_to_httpx_tuples": ".file", + "create_logger": ".logging", + "encode_query": ".query_encoder", + "jsonable_encoder": ".jsonable_encoder", + "parse_obj_as": ".pydantic_utilities", + "parse_rfc2822_datetime": ".datetime_utils", + "remove_none_from_dict": ".remove_none_from_dict", + "serialize_datetime": ".datetime_utils", + "universal_field_validator": ".pydantic_utilities", + "universal_root_validator": ".pydantic_utilities", + "update_forward_refs": ".pydantic_utilities", + "with_content_type": ".file", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + __all__ = [ "ApiError", @@ -29,20 +93,29 @@ "AsyncHttpClient", "AsyncHttpResponse", "BaseClientWrapper", + "ConsoleLogger", "FieldMetadata", "File", "HttpClient", "HttpResponse", + "ILogger", "IS_PYDANTIC_V2", + "LogConfig", + "LogLevel", + "Logger", + "ParsingError", "RequestOptions", + "Rfc2822DateTime", "SyncClientWrapper", "UniversalBaseModel", "UniversalRootModel", "convert_and_respect_annotation_metadata", "convert_file_dict_to_httpx_tuples", + "create_logger", "encode_query", "jsonable_encoder", "parse_obj_as", + "parse_rfc2822_datetime", "remove_none_from_dict", "serialize_datetime", "universal_field_validator", diff --git a/src/newscatcher/core/client_wrapper.py b/src/newscatcher/core/client_wrapper.py index 2c185f8..8bdcfd0 100644 --- a/src/newscatcher/core/client_wrapper.py +++ b/src/newscatcher/core/client_wrapper.py @@ -4,6 +4,7 @@ import httpx from .http_client import AsyncHttpClient, HttpClient +from .logging import LogConfig, Logger class BaseClientWrapper: @@ -14,18 +15,24 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, base_url: str, timeout: typing.Optional[float] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): self.api_key = api_key self._headers = headers self._base_url = base_url self._timeout = timeout + self._logging = logging def get_headers(self) -> typing.Dict[str, str]: + import platform + headers: typing.Dict[str, str] = { - "User-Agent": "newscatcher-sdk/1.4.0", + "User-Agent": "newscatcher-sdk/2.0.0", "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-sdk", - "X-Fern-SDK-Version": "1.4.0", + "X-Fern-SDK-Version": "2.0.0", **(self.get_custom_headers() or {}), } headers["x-api-token"] = self.api_key @@ -49,14 +56,16 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, base_url: str, timeout: typing.Optional[float] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, httpx_client: httpx.Client, ): - super().__init__(api_key=api_key, headers=headers, base_url=base_url, timeout=timeout) + super().__init__(api_key=api_key, headers=headers, base_url=base_url, timeout=timeout, logging=logging) self.httpx_client = HttpClient( httpx_client=httpx_client, base_headers=self.get_headers, base_timeout=self.get_timeout, base_url=self.get_base_url, + logging_config=self._logging, ) @@ -68,12 +77,24 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, base_url: str, timeout: typing.Optional[float] = None, + logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, + async_token: typing.Optional[typing.Callable[[], typing.Awaitable[str]]] = None, httpx_client: httpx.AsyncClient, ): - super().__init__(api_key=api_key, headers=headers, base_url=base_url, timeout=timeout) + super().__init__(api_key=api_key, headers=headers, base_url=base_url, timeout=timeout, logging=logging) + self._async_token = async_token self.httpx_client = AsyncHttpClient( httpx_client=httpx_client, base_headers=self.get_headers, base_timeout=self.get_timeout, base_url=self.get_base_url, + async_base_headers=self.async_get_headers, + logging_config=self._logging, ) + + async def async_get_headers(self) -> typing.Dict[str, str]: + headers = self.get_headers() + if self._async_token is not None: + token = await self._async_token() + headers["Authorization"] = f"Bearer {token}" + return headers diff --git a/src/newscatcher/core/datetime_utils.py b/src/newscatcher/core/datetime_utils.py index 7c9864a..a12b2ad 100644 --- a/src/newscatcher/core/datetime_utils.py +++ b/src/newscatcher/core/datetime_utils.py @@ -1,6 +1,48 @@ # This file was auto-generated by Fern from our API Definition. import datetime as dt +from email.utils import parsedate_to_datetime +from typing import Any + +import pydantic + +IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") + + +def parse_rfc2822_datetime(v: Any) -> dt.datetime: + """ + Parse an RFC 2822 datetime string (e.g., "Wed, 02 Oct 2002 13:00:00 GMT") + into a datetime object. If the value is already a datetime, return it as-is. + Falls back to ISO 8601 parsing if RFC 2822 parsing fails. + """ + if isinstance(v, dt.datetime): + return v + if isinstance(v, str): + try: + return parsedate_to_datetime(v) + except Exception: + pass + # Fallback to ISO 8601 parsing + return dt.datetime.fromisoformat(v.replace("Z", "+00:00")) + raise ValueError(f"Expected str or datetime, got {type(v)}") + + +class Rfc2822DateTime(dt.datetime): + """A datetime subclass that parses RFC 2822 date strings. + + On Pydantic V1, uses __get_validators__ for pre-validation. + On Pydantic V2, uses __get_pydantic_core_schema__ for BeforeValidator-style parsing. + """ + + @classmethod + def __get_validators__(cls): # type: ignore[no-untyped-def] + yield parse_rfc2822_datetime + + @classmethod + def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> Any: # type: ignore[override] + from pydantic_core import core_schema + + return core_schema.no_info_before_validator_function(parse_rfc2822_datetime, core_schema.datetime_schema()) def serialize_datetime(v: dt.datetime) -> str: diff --git a/src/newscatcher/core/force_multipart.py b/src/newscatcher/core/force_multipart.py index ae24ccf..5440913 100644 --- a/src/newscatcher/core/force_multipart.py +++ b/src/newscatcher/core/force_multipart.py @@ -1,7 +1,9 @@ # This file was auto-generated by Fern from our API Definition. +from typing import Any, Dict -class ForceMultipartDict(dict): + +class ForceMultipartDict(Dict[str, Any]): """ A dictionary subclass that always evaluates to True in boolean contexts. @@ -9,7 +11,7 @@ class ForceMultipartDict(dict): the dictionary is empty, which would normally evaluate to False. """ - def __bool__(self): + def __bool__(self) -> bool: return True diff --git a/src/newscatcher/core/http_client.py b/src/newscatcher/core/http_client.py index e4173f9..ee93758 100644 --- a/src/newscatcher/core/http_client.py +++ b/src/newscatcher/core/http_client.py @@ -5,7 +5,6 @@ import re import time import typing -import urllib.parse from contextlib import asynccontextmanager, contextmanager from random import random @@ -13,14 +12,15 @@ from .file import File, convert_file_dict_to_httpx_tuples from .force_multipart import FORCE_MULTIPART from .jsonable_encoder import jsonable_encoder +from .logging import LogConfig, Logger, create_logger from .query_encoder import encode_query -from .remove_none_from_dict import remove_none_from_dict +from .remove_none_from_dict import remove_none_from_dict as remove_none_from_dict from .request_options import RequestOptions from httpx._types import RequestFiles -INITIAL_RETRY_DELAY_SECONDS = 0.5 -MAX_RETRY_DELAY_SECONDS = 10 -MAX_RETRY_DELAY_SECONDS_FROM_HEADER = 30 +INITIAL_RETRY_DELAY_SECONDS = 1.0 +MAX_RETRY_DELAY_SECONDS = 60.0 +JITTER_FACTOR = 0.2 # 20% random jitter def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float]: @@ -64,6 +64,38 @@ def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float return seconds +def _add_positive_jitter(delay: float) -> float: + """Add positive jitter (0-20%) to prevent thundering herd.""" + jitter_multiplier = 1 + random() * JITTER_FACTOR + return delay * jitter_multiplier + + +def _add_symmetric_jitter(delay: float) -> float: + """Add symmetric jitter (±10%) for exponential backoff.""" + jitter_multiplier = 1 + (random() - 0.5) * JITTER_FACTOR + return delay * jitter_multiplier + + +def _parse_x_ratelimit_reset(response_headers: httpx.Headers) -> typing.Optional[float]: + """ + Parse the X-RateLimit-Reset header (Unix timestamp in seconds). + Returns seconds to wait, or None if header is missing/invalid. + """ + reset_time_str = response_headers.get("x-ratelimit-reset") + if reset_time_str is None: + return None + + try: + reset_time = int(reset_time_str) + delay = reset_time - time.time() + if delay > 0: + return delay + except (ValueError, TypeError): + pass + + return None + + def _retry_timeout(response: httpx.Response, retries: int) -> float: """ Determine the amount of time to wait before retrying a request. @@ -71,17 +103,19 @@ def _retry_timeout(response: httpx.Response, retries: int) -> float: with a jitter to determine the number of seconds to wait. """ - # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says. + # 1. Check Retry-After header first retry_after = _parse_retry_after(response.headers) - if retry_after is not None and retry_after <= MAX_RETRY_DELAY_SECONDS_FROM_HEADER: - return retry_after + if retry_after is not None and retry_after > 0: + return min(retry_after, MAX_RETRY_DELAY_SECONDS) - # Apply exponential backoff, capped at MAX_RETRY_DELAY_SECONDS. - retry_delay = min(INITIAL_RETRY_DELAY_SECONDS * pow(2.0, retries), MAX_RETRY_DELAY_SECONDS) + # 2. Check X-RateLimit-Reset header (with positive jitter) + ratelimit_reset = _parse_x_ratelimit_reset(response.headers) + if ratelimit_reset is not None: + return _add_positive_jitter(min(ratelimit_reset, MAX_RETRY_DELAY_SECONDS)) - # Add a randomness / jitter to the retry delay to avoid overwhelming the server with retries. - timeout = retry_delay * (1 - 0.25 * random()) - return timeout if timeout >= 0 else 0 + # 3. Fall back to exponential backoff (with symmetric jitter) + backoff = min(INITIAL_RETRY_DELAY_SECONDS * pow(2.0, retries), MAX_RETRY_DELAY_SECONDS) + return _add_symmetric_jitter(backoff) def _should_retry(response: httpx.Response) -> bool: @@ -89,6 +123,71 @@ def _should_retry(response: httpx.Response) -> bool: return response.status_code >= 500 or response.status_code in retryable_400s +_SENSITIVE_HEADERS = frozenset( + { + "authorization", + "www-authenticate", + "x-api-key", + "api-key", + "apikey", + "x-api-token", + "x-auth-token", + "auth-token", + "cookie", + "set-cookie", + "proxy-authorization", + "proxy-authenticate", + "x-csrf-token", + "x-xsrf-token", + "x-session-token", + "x-access-token", + } +) + + +def _redact_headers(headers: typing.Dict[str, str]) -> typing.Dict[str, str]: + return {k: ("[REDACTED]" if k.lower() in _SENSITIVE_HEADERS else v) for k, v in headers.items()} + + +def _build_url(base_url: str, path: typing.Optional[str]) -> str: + """ + Build a full URL by joining a base URL with a path. + + This function correctly handles base URLs that contain path prefixes (e.g., tenant-based URLs) + by using string concatenation instead of urllib.parse.urljoin(), which would incorrectly + strip path components when the path starts with '/'. + + Example: + >>> _build_url("https://cloud.example.com/org/tenant/api", "/users") + 'https://cloud.example.com/org/tenant/api/users' + + Args: + base_url: The base URL, which may contain path prefixes. + path: The path to append. Can be None or empty string. + + Returns: + The full URL with base_url and path properly joined. + """ + if not path: + return base_url + return f"{base_url.rstrip('/')}/{path.lstrip('/')}" + + +def _maybe_filter_none_from_multipart_data( + data: typing.Optional[typing.Any], + request_files: typing.Optional[RequestFiles], + force_multipart: typing.Optional[bool], +) -> typing.Optional[typing.Any]: + """ + Filter None values from data body for multipart/form requests. + This prevents httpx from converting None to empty strings in multipart encoding. + Only applies when files are present or force_multipart is True. + """ + if data is not None and isinstance(data, typing.Mapping) and (request_files or force_multipart): + return remove_none_from_dict(data) + return data + + def remove_omit_from_dict( original: typing.Dict[str, typing.Optional[typing.Any]], omit: typing.Optional[typing.Any], @@ -143,8 +242,19 @@ def get_request_body( # If both data and json are None, we send json data in the event extra properties are specified json_body = maybe_filter_request_body(json, request_options, omit) - # If you have an empty JSON body, you should just send None - return (json_body if json_body != {} else None), data_body if data_body != {} else None + has_additional_body_parameters = bool( + request_options is not None and request_options.get("additional_body_parameters") + ) + + # Only collapse empty dict to None when the body was not explicitly provided + # and there are no additional body parameters. This preserves explicit empty + # bodies (e.g., when an endpoint has a request body type but all fields are optional). + if json_body == {} and json is None and not has_additional_body_parameters: + json_body = None + if data_body == {} and data is None and not has_additional_body_parameters: + data_body = None + + return json_body, data_body class HttpClient: @@ -155,11 +265,13 @@ def __init__( base_timeout: typing.Callable[[], typing.Optional[float]], base_headers: typing.Callable[[], typing.Dict[str, str]], base_url: typing.Optional[typing.Callable[[], str]] = None, + logging_config: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): self.base_url = base_url self.base_timeout = base_timeout self.base_headers = base_headers self.httpx_client = httpx_client + self.logger = create_logger(logging_config) def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: base_url = maybe_base_url @@ -188,7 +300,7 @@ def request( ] = None, headers: typing.Optional[typing.Dict[str, typing.Any]] = None, request_options: typing.Optional[RequestOptions] = None, - retries: int = 2, + retries: int = 0, omit: typing.Optional[typing.Any] = None, force_multipart: typing.Optional[bool] = None, ) -> httpx.Response: @@ -210,35 +322,53 @@ def request( if (request_files is None or len(request_files) == 0) and force_multipart: request_files = FORCE_MULTIPART - response = self.httpx_client.request( - method=method, - url=urllib.parse.urljoin(f"{base_url}/", path), - headers=jsonable_encoder( + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( remove_none_from_dict( - { - **self.base_headers(), - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), - } - ) - ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, ) ) - ), + ) + ) + + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + has_body=json_body is not None or data_body is not None, + ) + + response = self.httpx_client.request( + method=method, + url=_request_url, + headers=_request_headers, + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -246,9 +376,9 @@ def request( timeout=timeout, ) - max_retries: int = request_options.get("max_retries", 0) if request_options is not None else 0 + max_retries: int = request_options.get("max_retries", 2) if request_options is not None else 2 if _should_retry(response=response): - if max_retries > retries: + if retries < max_retries: time.sleep(_retry_timeout(response=response, retries=retries)) return self.request( path=path, @@ -264,6 +394,24 @@ def request( omit=omit, ) + if self.logger.is_debug(): + if 200 <= response.status_code < 400: + self.logger.debug( + "HTTP request succeeded", + method=method, + url=_request_url, + status_code=response.status_code, + ) + + if self.logger.is_error(): + if response.status_code >= 400: + self.logger.error( + "HTTP request failed with error status", + method=method, + url=_request_url, + status_code=response.status_code, + ) + return response @contextmanager @@ -285,7 +433,7 @@ def stream( ] = None, headers: typing.Optional[typing.Dict[str, typing.Any]] = None, request_options: typing.Optional[RequestOptions] = None, - retries: int = 2, + retries: int = 0, omit: typing.Optional[typing.Any] = None, force_multipart: typing.Optional[bool] = None, ) -> typing.Iterator[httpx.Response]: @@ -307,35 +455,52 @@ def stream( json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) - with self.httpx_client.stream( - method=method, - url=urllib.parse.urljoin(f"{base_url}/", path), - headers=jsonable_encoder( + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( remove_none_from_dict( - { - **self.base_headers(), - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) if request_options is not None else {}), - } - ) - ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit, - ) + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, ) ) - ), + ) + ) + + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making streaming HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + ) + + with self.httpx_client.stream( + method=method, + url=_request_url, + headers=_request_headers, + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -353,11 +518,20 @@ def __init__( base_timeout: typing.Callable[[], typing.Optional[float]], base_headers: typing.Callable[[], typing.Dict[str, str]], base_url: typing.Optional[typing.Callable[[], str]] = None, + async_base_headers: typing.Optional[typing.Callable[[], typing.Awaitable[typing.Dict[str, str]]]] = None, + logging_config: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): self.base_url = base_url self.base_timeout = base_timeout self.base_headers = base_headers + self.async_base_headers = async_base_headers self.httpx_client = httpx_client + self.logger = create_logger(logging_config) + + async def _get_headers(self) -> typing.Dict[str, str]: + if self.async_base_headers is not None: + return await self.async_base_headers() + return self.base_headers() def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: base_url = maybe_base_url @@ -386,7 +560,7 @@ async def request( ] = None, headers: typing.Optional[typing.Dict[str, typing.Any]] = None, request_options: typing.Optional[RequestOptions] = None, - retries: int = 2, + retries: int = 0, omit: typing.Optional[typing.Any] = None, force_multipart: typing.Optional[bool] = None, ) -> httpx.Response: @@ -408,36 +582,56 @@ async def request( json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) - # Add the input to each of these and do None-safety checks - response = await self.httpx_client.request( - method=method, - url=urllib.parse.urljoin(f"{base_url}/", path), - headers=jsonable_encoder( + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Get headers (supports async token providers) + _headers = await self._get_headers() + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( remove_none_from_dict( - { - **self.base_headers(), - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), - } - ) - ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) or {} - if request_options is not None - else {} - ), - }, - omit, - ) + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, ) ) - ), + ) + ) + + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **_headers, + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + has_body=json_body is not None or data_body is not None, + ) + + response = await self.httpx_client.request( + method=method, + url=_request_url, + headers=_request_headers, + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, @@ -445,9 +639,9 @@ async def request( timeout=timeout, ) - max_retries: int = request_options.get("max_retries", 0) if request_options is not None else 0 + max_retries: int = request_options.get("max_retries", 2) if request_options is not None else 2 if _should_retry(response=response): - if max_retries > retries: + if retries < max_retries: await asyncio.sleep(_retry_timeout(response=response, retries=retries)) return await self.request( path=path, @@ -462,6 +656,25 @@ async def request( retries=retries + 1, omit=omit, ) + + if self.logger.is_debug(): + if 200 <= response.status_code < 400: + self.logger.debug( + "HTTP request succeeded", + method=method, + url=_request_url, + status_code=response.status_code, + ) + + if self.logger.is_error(): + if response.status_code >= 400: + self.logger.error( + "HTTP request failed with error status", + method=method, + url=_request_url, + status_code=response.status_code, + ) + return response @asynccontextmanager @@ -483,7 +696,7 @@ async def stream( ] = None, headers: typing.Optional[typing.Dict[str, typing.Any]] = None, request_options: typing.Optional[RequestOptions] = None, - retries: int = 2, + retries: int = 0, omit: typing.Optional[typing.Any] = None, force_multipart: typing.Optional[bool] = None, ) -> typing.AsyncIterator[httpx.Response]: @@ -505,35 +718,55 @@ async def stream( json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) - async with self.httpx_client.stream( - method=method, - url=urllib.parse.urljoin(f"{base_url}/", path), - headers=jsonable_encoder( + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Get headers (supports async token providers) + _headers = await self._get_headers() + + # Compute encoded params separately to avoid passing empty list to httpx + # (httpx strips existing query params from URL when params=[] is passed) + _encoded_params = encode_query( + jsonable_encoder( remove_none_from_dict( - { - **self.base_headers(), - **(headers if headers is not None else {}), - **(request_options.get("additional_headers", {}) if request_options is not None else {}), - } - ) - ), - params=encode_query( - jsonable_encoder( - remove_none_from_dict( - remove_omit_from_dict( - { - **(params if params is not None else {}), - **( - request_options.get("additional_query_parameters", {}) - if request_options is not None - else {} - ), - }, - omit=omit, - ) + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, ) ) - ), + ) + ) + + _request_url = _build_url(base_url, path) + _request_headers = jsonable_encoder( + remove_none_from_dict( + { + **_headers, + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ) + + if self.logger.is_debug(): + self.logger.debug( + "Making streaming HTTP request", + method=method, + url=_request_url, + headers=_redact_headers(_request_headers), + ) + + async with self.httpx_client.stream( + method=method, + url=_request_url, + headers=_request_headers, + params=_encoded_params if _encoded_params else None, json=json_body, data=data_body, content=content, diff --git a/src/newscatcher/core/http_response.py b/src/newscatcher/core/http_response.py index 48a1798..00bb109 100644 --- a/src/newscatcher/core/http_response.py +++ b/src/newscatcher/core/http_response.py @@ -4,12 +4,12 @@ import httpx +# Generic to represent the underlying type of the data wrapped by the HTTP response. T = TypeVar("T") -"""Generic to represent the underlying type of the data wrapped by the HTTP response.""" class BaseHttpResponse: - """Minimalist HTTP response wrapper that exposes response headers.""" + """Minimalist HTTP response wrapper that exposes response headers and status code.""" _response: httpx.Response @@ -20,6 +20,10 @@ def __init__(self, response: httpx.Response): def headers(self) -> Dict[str, str]: return dict(self._response.headers) + @property + def status_code(self) -> int: + return self._response.status_code + class HttpResponse(Generic[T], BaseHttpResponse): """HTTP response wrapper that exposes response headers and data.""" diff --git a/src/newscatcher/core/http_sse/__init__.py b/src/newscatcher/core/http_sse/__init__.py new file mode 100644 index 0000000..730e5a3 --- /dev/null +++ b/src/newscatcher/core/http_sse/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from ._api import EventSource, aconnect_sse, connect_sse + from ._exceptions import SSEError + from ._models import ServerSentEvent +_dynamic_imports: typing.Dict[str, str] = { + "EventSource": "._api", + "SSEError": "._exceptions", + "ServerSentEvent": "._models", + "aconnect_sse": "._api", + "connect_sse": "._api", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["EventSource", "SSEError", "ServerSentEvent", "aconnect_sse", "connect_sse"] diff --git a/src/newscatcher/core/http_sse/_api.py b/src/newscatcher/core/http_sse/_api.py new file mode 100644 index 0000000..f900b3b --- /dev/null +++ b/src/newscatcher/core/http_sse/_api.py @@ -0,0 +1,112 @@ +# This file was auto-generated by Fern from our API Definition. + +import re +from contextlib import asynccontextmanager, contextmanager +from typing import Any, AsyncGenerator, AsyncIterator, Iterator, cast + +import httpx +from ._decoders import SSEDecoder +from ._exceptions import SSEError +from ._models import ServerSentEvent + + +class EventSource: + def __init__(self, response: httpx.Response) -> None: + self._response = response + + def _check_content_type(self) -> None: + content_type = self._response.headers.get("content-type", "").partition(";")[0] + if "text/event-stream" not in content_type: + raise SSEError( + f"Expected response header Content-Type to contain 'text/event-stream', got {content_type!r}" + ) + + def _get_charset(self) -> str: + """Extract charset from Content-Type header, fallback to UTF-8.""" + content_type = self._response.headers.get("content-type", "") + + # Parse charset parameter using regex + charset_match = re.search(r"charset=([^;\s]+)", content_type, re.IGNORECASE) + if charset_match: + charset = charset_match.group(1).strip("\"'") + # Validate that it's a known encoding + try: + # Test if the charset is valid by trying to encode/decode + "test".encode(charset).decode(charset) + return charset + except (LookupError, UnicodeError): + # If charset is invalid, fall back to UTF-8 + pass + + # Default to UTF-8 if no charset specified or invalid charset + return "utf-8" + + @property + def response(self) -> httpx.Response: + return self._response + + def iter_sse(self) -> Iterator[ServerSentEvent]: + self._check_content_type() + decoder = SSEDecoder() + charset = self._get_charset() + + buffer = "" + for chunk in self._response.iter_bytes(): + # Decode chunk using detected charset + text_chunk = chunk.decode(charset, errors="replace") + buffer += text_chunk + + # Process complete lines + while "\n" in buffer: + line, buffer = buffer.split("\n", 1) + line = line.rstrip("\r") + sse = decoder.decode(line) + # when we reach a "\n\n" => line = '' + # => decoder will attempt to return an SSE Event + if sse is not None: + yield sse + + # Process any remaining data in buffer + if buffer.strip(): + line = buffer.rstrip("\r") + sse = decoder.decode(line) + if sse is not None: + yield sse + + async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]: + self._check_content_type() + decoder = SSEDecoder() + lines = cast(AsyncGenerator[str, None], self._response.aiter_lines()) + try: + async for line in lines: + line = line.rstrip("\n") + sse = decoder.decode(line) + if sse is not None: + yield sse + finally: + await lines.aclose() + + +@contextmanager +def connect_sse(client: httpx.Client, method: str, url: str, **kwargs: Any) -> Iterator[EventSource]: + headers = kwargs.pop("headers", {}) + headers["Accept"] = "text/event-stream" + headers["Cache-Control"] = "no-store" + + with client.stream(method, url, headers=headers, **kwargs) as response: + yield EventSource(response) + + +@asynccontextmanager +async def aconnect_sse( + client: httpx.AsyncClient, + method: str, + url: str, + **kwargs: Any, +) -> AsyncIterator[EventSource]: + headers = kwargs.pop("headers", {}) + headers["Accept"] = "text/event-stream" + headers["Cache-Control"] = "no-store" + + async with client.stream(method, url, headers=headers, **kwargs) as response: + yield EventSource(response) diff --git a/src/newscatcher/core/http_sse/_decoders.py b/src/newscatcher/core/http_sse/_decoders.py new file mode 100644 index 0000000..339b089 --- /dev/null +++ b/src/newscatcher/core/http_sse/_decoders.py @@ -0,0 +1,61 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import List, Optional + +from ._models import ServerSentEvent + + +class SSEDecoder: + def __init__(self) -> None: + self._event = "" + self._data: List[str] = [] + self._last_event_id = "" + self._retry: Optional[int] = None + + def decode(self, line: str) -> Optional[ServerSentEvent]: + # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 + + if not line: + if not self._event and not self._data and not self._last_event_id and self._retry is None: + return None + + sse = ServerSentEvent( + event=self._event, + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + + # NOTE: as per the SSE spec, do not reset last_event_id. + self._event = "" + self._data = [] + self._retry = None + + return sse + + if line.startswith(":"): + return None + + fieldname, _, value = line.partition(":") + + if value.startswith(" "): + value = value[1:] + + if fieldname == "event": + self._event = value + elif fieldname == "data": + self._data.append(value) + elif fieldname == "id": + if "\0" in value: + pass + else: + self._last_event_id = value + elif fieldname == "retry": + try: + self._retry = int(value) + except (TypeError, ValueError): + pass + else: + pass # Field is ignored. + + return None diff --git a/src/newscatcher/types/similar_documents_fields.py b/src/newscatcher/core/http_sse/_exceptions.py similarity index 51% rename from src/newscatcher/types/similar_documents_fields.py rename to src/newscatcher/core/http_sse/_exceptions.py index 159850f..81605a8 100644 --- a/src/newscatcher/types/similar_documents_fields.py +++ b/src/newscatcher/core/http_sse/_exceptions.py @@ -1,3 +1,7 @@ # This file was auto-generated by Fern from our API Definition. -SimilarDocumentsFields = str +import httpx + + +class SSEError(httpx.TransportError): + pass diff --git a/src/newscatcher/core/http_sse/_models.py b/src/newscatcher/core/http_sse/_models.py new file mode 100644 index 0000000..1af57f8 --- /dev/null +++ b/src/newscatcher/core/http_sse/_models.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import json +from dataclasses import dataclass +from typing import Any, Optional + + +@dataclass(frozen=True) +class ServerSentEvent: + event: str = "message" + data: str = "" + id: str = "" + retry: Optional[int] = None + + def json(self) -> Any: + """Parse the data field as JSON.""" + return json.loads(self.data) diff --git a/src/newscatcher/core/jsonable_encoder.py b/src/newscatcher/core/jsonable_encoder.py index afee366..f8beaea 100644 --- a/src/newscatcher/core/jsonable_encoder.py +++ b/src/newscatcher/core/jsonable_encoder.py @@ -30,6 +30,10 @@ def jsonable_encoder(obj: Any, custom_encoder: Optional[Dict[Any, Callable[[Any], Any]]] = None) -> Any: custom_encoder = custom_encoder or {} + # Generated SDKs use Ellipsis (`...`) as the sentinel value for "OMIT". + # OMIT values should be excluded from serialized payloads. + if obj is Ellipsis: + return None if custom_encoder: if type(obj) in custom_encoder: return custom_encoder[type(obj)](obj) @@ -70,6 +74,8 @@ def jsonable_encoder(obj: Any, custom_encoder: Optional[Dict[Any, Callable[[Any] allowed_keys = set(obj.keys()) for key, value in obj.items(): if key in allowed_keys: + if value is Ellipsis: + continue encoded_key = jsonable_encoder(key, custom_encoder=custom_encoder) encoded_value = jsonable_encoder(value, custom_encoder=custom_encoder) encoded_dict[encoded_key] = encoded_value @@ -77,6 +83,8 @@ def jsonable_encoder(obj: Any, custom_encoder: Optional[Dict[Any, Callable[[Any] if isinstance(obj, (list, set, frozenset, GeneratorType, tuple)): encoded_list = [] for item in obj: + if item is Ellipsis: + continue encoded_list.append(jsonable_encoder(item, custom_encoder=custom_encoder)) return encoded_list diff --git a/src/newscatcher/core/logging.py b/src/newscatcher/core/logging.py new file mode 100644 index 0000000..e5e5724 --- /dev/null +++ b/src/newscatcher/core/logging.py @@ -0,0 +1,107 @@ +# This file was auto-generated by Fern from our API Definition. + +import logging +import typing + +LogLevel = typing.Literal["debug", "info", "warn", "error"] + +_LOG_LEVEL_MAP: typing.Dict[LogLevel, int] = { + "debug": 1, + "info": 2, + "warn": 3, + "error": 4, +} + + +class ILogger(typing.Protocol): + def debug(self, message: str, **kwargs: typing.Any) -> None: ... + def info(self, message: str, **kwargs: typing.Any) -> None: ... + def warn(self, message: str, **kwargs: typing.Any) -> None: ... + def error(self, message: str, **kwargs: typing.Any) -> None: ... + + +class ConsoleLogger: + _logger: logging.Logger + + def __init__(self) -> None: + self._logger = logging.getLogger("fern") + if not self._logger.handlers: + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter("%(levelname)s - %(message)s")) + self._logger.addHandler(handler) + self._logger.setLevel(logging.DEBUG) + + def debug(self, message: str, **kwargs: typing.Any) -> None: + self._logger.debug(message, extra=kwargs) + + def info(self, message: str, **kwargs: typing.Any) -> None: + self._logger.info(message, extra=kwargs) + + def warn(self, message: str, **kwargs: typing.Any) -> None: + self._logger.warning(message, extra=kwargs) + + def error(self, message: str, **kwargs: typing.Any) -> None: + self._logger.error(message, extra=kwargs) + + +class LogConfig(typing.TypedDict, total=False): + level: LogLevel + logger: ILogger + silent: bool + + +class Logger: + _level: int + _logger: ILogger + _silent: bool + + def __init__(self, *, level: LogLevel, logger: ILogger, silent: bool) -> None: + self._level = _LOG_LEVEL_MAP[level] + self._logger = logger + self._silent = silent + + def _should_log(self, level: LogLevel) -> bool: + return not self._silent and self._level <= _LOG_LEVEL_MAP[level] + + def is_debug(self) -> bool: + return self._should_log("debug") + + def is_info(self) -> bool: + return self._should_log("info") + + def is_warn(self) -> bool: + return self._should_log("warn") + + def is_error(self) -> bool: + return self._should_log("error") + + def debug(self, message: str, **kwargs: typing.Any) -> None: + if self.is_debug(): + self._logger.debug(message, **kwargs) + + def info(self, message: str, **kwargs: typing.Any) -> None: + if self.is_info(): + self._logger.info(message, **kwargs) + + def warn(self, message: str, **kwargs: typing.Any) -> None: + if self.is_warn(): + self._logger.warn(message, **kwargs) + + def error(self, message: str, **kwargs: typing.Any) -> None: + if self.is_error(): + self._logger.error(message, **kwargs) + + +_default_logger: Logger = Logger(level="info", logger=ConsoleLogger(), silent=True) + + +def create_logger(config: typing.Optional[typing.Union[LogConfig, Logger]] = None) -> Logger: + if config is None: + return _default_logger + if isinstance(config, Logger): + return config + return Logger( + level=config.get("level", "info"), + logger=config.get("logger", ConsoleLogger()), + silent=config.get("silent", True), + ) diff --git a/src/newscatcher/core/parse_error.py b/src/newscatcher/core/parse_error.py new file mode 100644 index 0000000..4527c6a --- /dev/null +++ b/src/newscatcher/core/parse_error.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, Optional + + +class ParsingError(Exception): + """ + Raised when the SDK fails to parse/validate a response from the server. + This typically indicates that the server returned a response whose shape + does not match the expected schema. + """ + + headers: Optional[Dict[str, str]] + status_code: Optional[int] + body: Any + cause: Optional[Exception] + + def __init__( + self, + *, + headers: Optional[Dict[str, str]] = None, + status_code: Optional[int] = None, + body: Any = None, + cause: Optional[Exception] = None, + ) -> None: + self.headers = headers + self.status_code = status_code + self.body = body + self.cause = cause + super().__init__() + if cause is not None: + self.__cause__ = cause + + def __str__(self) -> str: + cause_str = f", cause: {self.cause}" if self.cause is not None else "" + return f"headers: {self.headers}, status_code: {self.status_code}, body: {self.body}{cause_str}" diff --git a/src/newscatcher/core/pydantic_utilities.py b/src/newscatcher/core/pydantic_utilities.py index 7db2950..fea3a08 100644 --- a/src/newscatcher/core/pydantic_utilities.py +++ b/src/newscatcher/core/pydantic_utilities.py @@ -2,30 +2,129 @@ # nopycln: file import datetime as dt +import inspect +import json +import logging from collections import defaultdict -from typing import Any, Callable, ClassVar, Dict, List, Mapping, Optional, Set, Tuple, Type, TypeVar, Union, cast +from dataclasses import asdict +from typing import ( + TYPE_CHECKING, + Any, + Callable, + ClassVar, + Dict, + List, + Mapping, + Optional, + Set, + Tuple, + Type, + TypeVar, + Union, + cast, +) import pydantic +import typing_extensions +from pydantic.fields import FieldInfo as _FieldInfo + +_logger = logging.getLogger(__name__) + +if TYPE_CHECKING: + from .http_sse._models import ServerSentEvent IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") if IS_PYDANTIC_V2: - from pydantic.v1.datetime_parse import parse_date as parse_date - from pydantic.v1.datetime_parse import parse_datetime as parse_datetime - from pydantic.v1.fields import ModelField as ModelField - from pydantic.v1.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[attr-defined] - from pydantic.v1.typing import get_args as get_args - from pydantic.v1.typing import get_origin as get_origin - from pydantic.v1.typing import is_literal_type as is_literal_type - from pydantic.v1.typing import is_union as is_union + _datetime_adapter = pydantic.TypeAdapter(dt.datetime) # type: ignore[attr-defined] + _date_adapter = pydantic.TypeAdapter(dt.date) # type: ignore[attr-defined] + + def parse_datetime(value: Any) -> dt.datetime: # type: ignore[misc] + if isinstance(value, dt.datetime): + return value + return _datetime_adapter.validate_python(value) + + def parse_date(value: Any) -> dt.date: # type: ignore[misc] + if isinstance(value, dt.datetime): + return value.date() + if isinstance(value, dt.date): + return value + return _date_adapter.validate_python(value) + + # Avoid importing from pydantic.v1 to maintain Python 3.14 compatibility. + from typing import get_args as get_args # type: ignore[assignment] + from typing import get_origin as get_origin # type: ignore[assignment] + + def is_literal_type(tp: Optional[Type[Any]]) -> bool: # type: ignore[misc] + return typing_extensions.get_origin(tp) is typing_extensions.Literal + + def is_union(tp: Optional[Type[Any]]) -> bool: # type: ignore[misc] + return tp is Union or typing_extensions.get_origin(tp) is Union # type: ignore[comparison-overlap] + + # Inline encoders_by_type to avoid importing from pydantic.v1.json + import re as _re + from collections import deque as _deque + from decimal import Decimal as _Decimal + from enum import Enum as _Enum + from ipaddress import ( + IPv4Address as _IPv4Address, + ) + from ipaddress import ( + IPv4Interface as _IPv4Interface, + ) + from ipaddress import ( + IPv4Network as _IPv4Network, + ) + from ipaddress import ( + IPv6Address as _IPv6Address, + ) + from ipaddress import ( + IPv6Interface as _IPv6Interface, + ) + from ipaddress import ( + IPv6Network as _IPv6Network, + ) + from pathlib import Path as _Path + from types import GeneratorType as _GeneratorType + from uuid import UUID as _UUID + + from pydantic.fields import FieldInfo as ModelField # type: ignore[no-redef, assignment] + + def _decimal_encoder(dec_value: Any) -> Any: + if dec_value.as_tuple().exponent >= 0: + return int(dec_value) + return float(dec_value) + + encoders_by_type: Dict[Type[Any], Callable[[Any], Any]] = { # type: ignore[no-redef] + bytes: lambda o: o.decode(), + dt.date: lambda o: o.isoformat(), + dt.datetime: lambda o: o.isoformat(), + dt.time: lambda o: o.isoformat(), + dt.timedelta: lambda td: td.total_seconds(), + _Decimal: _decimal_encoder, + _Enum: lambda o: o.value, + frozenset: list, + _deque: list, + _GeneratorType: list, + _IPv4Address: str, + _IPv4Interface: str, + _IPv4Network: str, + _IPv6Address: str, + _IPv6Interface: str, + _IPv6Network: str, + _Path: str, + _re.Pattern: lambda o: o.pattern, + set: list, + _UUID: str, + } else: from pydantic.datetime_parse import parse_date as parse_date # type: ignore[no-redef] from pydantic.datetime_parse import parse_datetime as parse_datetime # type: ignore[no-redef] - from pydantic.fields import ModelField as ModelField # type: ignore[attr-defined, no-redef] + from pydantic.fields import ModelField as ModelField # type: ignore[attr-defined, no-redef, assignment] from pydantic.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[no-redef] from pydantic.typing import get_args as get_args # type: ignore[no-redef] from pydantic.typing import get_origin as get_origin # type: ignore[no-redef] - from pydantic.typing import is_literal_type as is_literal_type # type: ignore[no-redef] + from pydantic.typing import is_literal_type as is_literal_type # type: ignore[no-redef, assignment] from pydantic.typing import is_union as is_union # type: ignore[no-redef] from .datetime_utils import serialize_datetime @@ -36,8 +135,212 @@ Model = TypeVar("Model", bound=pydantic.BaseModel) +def _get_discriminator_and_variants(type_: Type[Any]) -> Tuple[Optional[str], Optional[List[Type[Any]]]]: + """ + Extract the discriminator field name and union variants from a discriminated union type. + Supports Annotated[Union[...], Field(discriminator=...)] patterns. + Returns (discriminator, variants) or (None, None) if not a discriminated union. + """ + origin = typing_extensions.get_origin(type_) + + if origin is typing_extensions.Annotated: + args = typing_extensions.get_args(type_) + if len(args) >= 2: + inner_type = args[0] + # Check annotations for discriminator + discriminator = None + for annotation in args[1:]: + if hasattr(annotation, "discriminator"): + discriminator = getattr(annotation, "discriminator", None) + break + + if discriminator: + inner_origin = typing_extensions.get_origin(inner_type) + if inner_origin is Union: + variants = list(typing_extensions.get_args(inner_type)) + return discriminator, variants + return None, None + + +def _get_field_annotation(model: Type[Any], field_name: str) -> Optional[Type[Any]]: + """Get the type annotation of a field from a Pydantic model.""" + if IS_PYDANTIC_V2: + fields = getattr(model, "model_fields", {}) + field_info = fields.get(field_name) + if field_info: + return cast(Optional[Type[Any]], field_info.annotation) + else: + fields = getattr(model, "__fields__", {}) + field_info = fields.get(field_name) + if field_info: + return cast(Optional[Type[Any]], field_info.outer_type_) + return None + + +def _find_variant_by_discriminator( + variants: List[Type[Any]], + discriminator: str, + discriminator_value: Any, +) -> Optional[Type[Any]]: + """Find the union variant that matches the discriminator value.""" + for variant in variants: + if not (inspect.isclass(variant) and issubclass(variant, pydantic.BaseModel)): + continue + + disc_annotation = _get_field_annotation(variant, discriminator) + if disc_annotation and is_literal_type(disc_annotation): + literal_args = get_args(disc_annotation) + if literal_args and literal_args[0] == discriminator_value: + return variant + return None + + +def _is_string_type(type_: Type[Any]) -> bool: + """Check if a type is str or Optional[str].""" + if type_ is str: + return True + + origin = typing_extensions.get_origin(type_) + if origin is Union: + args = typing_extensions.get_args(type_) + # Optional[str] = Union[str, None] + non_none_args = [a for a in args if a is not type(None)] + if len(non_none_args) == 1 and non_none_args[0] is str: + return True + + return False + + +def parse_sse_obj(sse: "ServerSentEvent", type_: Type[T]) -> T: + """ + Parse a ServerSentEvent into the appropriate type. + + Handles two scenarios based on where the discriminator field is located: + + 1. Data-level discrimination: The discriminator (e.g., 'type') is inside the 'data' payload. + The union describes the data content, not the SSE envelope. + -> Returns: json.loads(data) parsed into the type + + Example: ChatStreamResponse with discriminator='type' + Input: ServerSentEvent(event="message", data='{"type": "content-delta", ...}', id="") + Output: ContentDeltaEvent (parsed from data, SSE envelope stripped) + + 2. Event-level discrimination: The discriminator (e.g., 'event') is at the SSE event level. + The union describes the full SSE event structure. + -> Returns: SSE envelope with 'data' field JSON-parsed only if the variant expects non-string + + Example: JobStreamResponse with discriminator='event' + Input: ServerSentEvent(event="ERROR", data='{"code": "FAILED", ...}', id="123") + Output: JobStreamResponse_Error with data as ErrorData object + + But for variants where data is str (like STATUS_UPDATE): + Input: ServerSentEvent(event="STATUS_UPDATE", data='{"status": "processing"}', id="1") + Output: JobStreamResponse_StatusUpdate with data as string (not parsed) + + Args: + sse: The ServerSentEvent object to parse + type_: The target discriminated union type + + Returns: + The parsed object of type T + + Note: + This function is only available in SDK contexts where http_sse module exists. + """ + sse_event = asdict(sse) + discriminator, variants = _get_discriminator_and_variants(type_) + + if discriminator is None or variants is None: + # Not a discriminated union - parse the data field as JSON + data_value = sse_event.get("data") + if isinstance(data_value, str) and data_value: + try: + parsed_data = json.loads(data_value) + return parse_obj_as(type_, parsed_data) + except json.JSONDecodeError as e: + _logger.warning( + "Failed to parse SSE data field as JSON: %s, data: %s", + e, + data_value[:100] if len(data_value) > 100 else data_value, + ) + return parse_obj_as(type_, sse_event) + + data_value = sse_event.get("data") + + # Check if discriminator is at the top level (event-level discrimination) + if discriminator in sse_event: + # Case 2: Event-level discrimination + # Find the matching variant to check if 'data' field needs JSON parsing + disc_value = sse_event.get(discriminator) + matching_variant = _find_variant_by_discriminator(variants, discriminator, disc_value) + + if matching_variant is not None: + # Check what type the variant expects for 'data' + data_type = _get_field_annotation(matching_variant, "data") + if data_type is not None and not _is_string_type(data_type): + # Variant expects non-string data - parse JSON + if isinstance(data_value, str) and data_value: + try: + parsed_data = json.loads(data_value) + new_object = dict(sse_event) + new_object["data"] = parsed_data + return parse_obj_as(type_, new_object) + except json.JSONDecodeError as e: + _logger.warning( + "Failed to parse SSE data field as JSON for event-level discrimination: %s, data: %s", + e, + data_value[:100] if len(data_value) > 100 else data_value, + ) + # Either no matching variant, data is string type, or JSON parse failed + return parse_obj_as(type_, sse_event) + + else: + # Case 1: Data-level discrimination + # The discriminator is inside the data payload - extract and parse data only + if isinstance(data_value, str) and data_value: + try: + parsed_data = json.loads(data_value) + return parse_obj_as(type_, parsed_data) + except json.JSONDecodeError as e: + _logger.warning( + "Failed to parse SSE data field as JSON for data-level discrimination: %s, data: %s", + e, + data_value[:100] if len(data_value) > 100 else data_value, + ) + return parse_obj_as(type_, sse_event) + + def parse_obj_as(type_: Type[T], object_: Any) -> T: - dealiased_object = convert_and_respect_annotation_metadata(object_=object_, annotation=type_, direction="read") + # convert_and_respect_annotation_metadata is required for TypedDict aliasing. + # + # For Pydantic models, whether we should pre-dealias depends on how the model encodes aliasing: + # - If the model uses real Pydantic aliases (pydantic.Field(alias=...)), then we must pass wire keys through + # unchanged so Pydantic can validate them. + # - If the model encodes aliasing only via FieldMetadata annotations, then we MUST pre-dealias because Pydantic + # will not recognize those aliases during validation. + if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): + has_pydantic_aliases = False + if IS_PYDANTIC_V2: + for field_name, field_info in getattr(type_, "model_fields", {}).items(): # type: ignore[attr-defined] + alias = getattr(field_info, "alias", None) + if alias is not None and alias != field_name: + has_pydantic_aliases = True + break + else: + for field in getattr(type_, "__fields__", {}).values(): + alias = getattr(field, "alias", None) + name = getattr(field, "name", None) + if alias is not None and name is not None and alias != name: + has_pydantic_aliases = True + break + + dealiased_object = ( + object_ + if has_pydantic_aliases + else convert_and_respect_annotation_metadata(object_=object_, annotation=type_, direction="read") + ) + else: + dealiased_object = convert_and_respect_annotation_metadata(object_=object_, annotation=type_, direction="read") if IS_PYDANTIC_V2: adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined] return adapter.validate_python(dealiased_object) @@ -59,9 +362,46 @@ class UniversalBaseModel(pydantic.BaseModel): protected_namespaces=(), ) + @pydantic.model_validator(mode="before") # type: ignore[attr-defined] + @classmethod + def _coerce_field_names_to_aliases(cls, data: Any) -> Any: + """ + Accept Python field names in input by rewriting them to their Pydantic aliases, + while avoiding silent collisions when a key could refer to multiple fields. + """ + if not isinstance(data, Mapping): + return data + + fields = getattr(cls, "model_fields", {}) # type: ignore[attr-defined] + name_to_alias: Dict[str, str] = {} + alias_to_name: Dict[str, str] = {} + + for name, field_info in fields.items(): + alias = getattr(field_info, "alias", None) or name + name_to_alias[name] = alias + if alias != name: + alias_to_name[alias] = name + + # Detect ambiguous keys: a key that is an alias for one field and a name for another. + ambiguous_keys = set(alias_to_name.keys()).intersection(set(name_to_alias.keys())) + for key in ambiguous_keys: + if key in data and name_to_alias[key] not in data: + raise ValueError( + f"Ambiguous input key '{key}': it is both a field name and an alias. " + "Provide the explicit alias key to disambiguate." + ) + + original_keys = set(data.keys()) + rewritten: Dict[str, Any] = dict(data) + for name, alias in name_to_alias.items(): + if alias != name and name in original_keys and alias not in rewritten: + rewritten[alias] = rewritten.pop(name) + + return rewritten + @pydantic.model_serializer(mode="plain", when_used="json") # type: ignore[attr-defined] def serialize_model(self) -> Any: # type: ignore[name-defined] - serialized = self.model_dump() + serialized = self.dict() # type: ignore[attr-defined] data = {k: serialize_datetime(v) if isinstance(v, dt.datetime) else v for k, v in serialized.items()} return data @@ -71,6 +411,40 @@ class Config: smart_union = True json_encoders = {dt.datetime: serialize_datetime} + @pydantic.root_validator(pre=True) + def _coerce_field_names_to_aliases(cls, values: Any) -> Any: + """ + Pydantic v1 equivalent of _coerce_field_names_to_aliases. + """ + if not isinstance(values, Mapping): + return values + + fields = getattr(cls, "__fields__", {}) + name_to_alias: Dict[str, str] = {} + alias_to_name: Dict[str, str] = {} + + for name, field in fields.items(): + alias = getattr(field, "alias", None) or name + name_to_alias[name] = alias + if alias != name: + alias_to_name[alias] = name + + ambiguous_keys = set(alias_to_name.keys()).intersection(set(name_to_alias.keys())) + for key in ambiguous_keys: + if key in values and name_to_alias[key] not in values: + raise ValueError( + f"Ambiguous input key '{key}': it is both a field name and an alias. " + "Provide the explicit alias key to disambiguate." + ) + + original_keys = set(values.keys()) + rewritten: Dict[str, Any] = dict(values) + for name, alias in name_to_alias.items(): + if alias != name and name in original_keys and alias not in rewritten: + rewritten[alias] = rewritten.pop(name) + + return rewritten + @classmethod def model_construct(cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any) -> "Model": dealiased_object = convert_and_respect_annotation_metadata(object_=values, annotation=cls, direction="read") @@ -147,7 +521,10 @@ def dict(self, **kwargs: Any) -> Dict[str, Any]: dict_dump = super().dict(**kwargs_with_defaults_exclude_unset_include_fields) - return convert_and_respect_annotation_metadata(object_=dict_dump, annotation=self.__class__, direction="write") + return cast( + Dict[str, Any], + convert_and_respect_annotation_metadata(object_=dict_dump, annotation=self.__class__, direction="write"), + ) def _union_list_of_pydantic_dicts(source: List[Any], destination: List[Any]) -> List[Any]: @@ -217,7 +594,9 @@ def universal_root_validator( ) -> Callable[[AnyCallable], AnyCallable]: def decorator(func: AnyCallable) -> AnyCallable: if IS_PYDANTIC_V2: - return cast(AnyCallable, pydantic.model_validator(mode="before" if pre else "after")(func)) # type: ignore[attr-defined] + # In Pydantic v2, for RootModel we always use "before" mode + # The custom validators transform the input value before the model is created + return cast(AnyCallable, pydantic.model_validator(mode="before")(func)) # type: ignore[attr-defined] return cast(AnyCallable, pydantic.root_validator(pre=pre)(func)) # type: ignore[call-overload] return decorator @@ -232,7 +611,7 @@ def decorator(func: AnyCallable) -> AnyCallable: return decorator -PydanticField = Union[ModelField, pydantic.fields.FieldInfo] +PydanticField = Union[ModelField, _FieldInfo] def _get_model_fields(model: Type["Model"]) -> Mapping[str, PydanticField]: diff --git a/src/newscatcher/errors/__init__.py b/src/newscatcher/errors/__init__.py index ac3639a..fee5ae7 100644 --- a/src/newscatcher/errors/__init__.py +++ b/src/newscatcher/errors/__init__.py @@ -2,13 +2,48 @@ # isort: skip_file -from .bad_request_error import BadRequestError -from .forbidden_error import ForbiddenError -from .internal_server_error import InternalServerError -from .request_timeout_error import RequestTimeoutError -from .too_many_requests_error import TooManyRequestsError -from .unauthorized_error import UnauthorizedError -from .unprocessable_entity_error import UnprocessableEntityError +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .bad_request_error import BadRequestError + from .forbidden_error import ForbiddenError + from .internal_server_error import InternalServerError + from .request_timeout_error import RequestTimeoutError + from .too_many_requests_error import TooManyRequestsError + from .unauthorized_error import UnauthorizedError + from .unprocessable_entity_error import UnprocessableEntityError +_dynamic_imports: typing.Dict[str, str] = { + "BadRequestError": ".bad_request_error", + "ForbiddenError": ".forbidden_error", + "InternalServerError": ".internal_server_error", + "RequestTimeoutError": ".request_timeout_error", + "TooManyRequestsError": ".too_many_requests_error", + "UnauthorizedError": ".unauthorized_error", + "UnprocessableEntityError": ".unprocessable_entity_error", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + __all__ = [ "BadRequestError", diff --git a/src/newscatcher/latest_headlines/__init__.py b/src/newscatcher/latest_headlines/__init__.py new file mode 100644 index 0000000..327d1f2 --- /dev/null +++ b/src/newscatcher/latest_headlines/__init__.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import LatestHeadlinesGetResponse, LatestHeadlinesPostResponse +_dynamic_imports: typing.Dict[str, str] = { + "LatestHeadlinesGetResponse": ".types", + "LatestHeadlinesPostResponse": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["LatestHeadlinesGetResponse", "LatestHeadlinesPostResponse"] diff --git a/src/newscatcher/latestheadlines/client.py b/src/newscatcher/latest_headlines/client.py similarity index 56% rename from src/newscatcher/latestheadlines/client.py rename to src/newscatcher/latest_headlines/client.py index ac90a8e..4577079 100644 --- a/src/newscatcher/latestheadlines/client.py +++ b/src/newscatcher/latest_headlines/client.py @@ -6,6 +6,7 @@ from ..core.request_options import RequestOptions from ..types.all_domain_links import AllDomainLinks from ..types.all_links import AllLinks +from ..types.all_links_text import AllLinksText from ..types.by_parse_date import ByParseDate from ..types.clustering_enabled import ClusteringEnabled from ..types.clustering_threshold import ClusteringThreshold @@ -40,6 +41,7 @@ from ..types.predefined_sources import PredefinedSources from ..types.ranked_only import RankedOnly from ..types.robots_compliant import RobotsCompliant +from ..types.sort_by import SortBy from ..types.sources import Sources from ..types.theme import Theme from ..types.title_sentiment_max import TitleSentimentMax @@ -47,8 +49,7 @@ from ..types.when import When from ..types.word_count_max import WordCountMax from ..types.word_count_min import WordCountMin -from .raw_client import AsyncRawLatestheadlinesClient, RawLatestheadlinesClient -from .types.latest_headlines_get_request_clustering_variable import LatestHeadlinesGetRequestClusteringVariable +from .raw_client import AsyncRawLatestHeadlinesClient, RawLatestHeadlinesClient from .types.latest_headlines_get_response import LatestHeadlinesGetResponse from .types.latest_headlines_post_response import LatestHeadlinesPostResponse @@ -56,26 +57,27 @@ OMIT = typing.cast(typing.Any, ...) -class LatestheadlinesClient: +class LatestHeadlinesClient: def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawLatestheadlinesClient(client_wrapper=client_wrapper) + self._raw_client = RawLatestHeadlinesClient(client_wrapper=client_wrapper) @property - def with_raw_response(self) -> RawLatestheadlinesClient: + def with_raw_response(self) -> RawLatestHeadlinesClient: """ Retrieves a raw implementation of this client that returns raw responses. Returns ------- - RawLatestheadlinesClient + RawLatestHeadlinesClient """ return self._raw_client - def get( + def latest_headlines_get( self, *, - when: typing.Optional[str] = None, - by_parse_date: typing.Optional[bool] = None, + when: typing.Optional[When] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, lang: typing.Optional[str] = None, not_lang: typing.Optional[str] = None, countries: typing.Optional[str] = None, @@ -84,39 +86,40 @@ def get( sources: typing.Optional[str] = None, not_sources: typing.Optional[str] = None, not_author_name: typing.Optional[str] = None, - ranked_only: typing.Optional[bool] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + ranked_only: typing.Optional[RankedOnly] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - clustering_enabled: typing.Optional[bool] = None, - clustering_variable: typing.Optional[LatestHeadlinesGetRequestClusteringVariable] = None, - clustering_threshold: typing.Optional[float] = None, + all_links_text: typing.Optional[str] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, + clustering_enabled: typing.Optional[ClusteringEnabled] = None, + clustering_variable: typing.Optional[ClusteringVariable] = None, + clustering_threshold: typing.Optional[ClusteringThreshold] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, iab_tags: typing.Optional[str] = None, not_iab_tags: typing.Optional[str] = None, custom_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> LatestHeadlinesGetResponse: """ @@ -124,143 +127,89 @@ def get( Parameters ---------- - when : typing.Optional[str] - The time period for which you want to get the latest headlines. + when : typing.Optional[When] - Format examples: - - `7d`: Last seven days - - `30d`: Last 30 days - - `1h`: Last hour - - `24h`: Last 24 hours + by_parse_date : typing.Optional[ByParseDate] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + sort_by : typing.Optional[SortBy] lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). - - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. - - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. - - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - page_size : typing.Optional[int] - The number of articles to return per page. + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - clustering_enabled : typing.Optional[bool] - Determines whether to group similar articles into clusters. If true, the API returns clustered results. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + word_count_min : typing.Optional[WordCountMin] - clustering_variable : typing.Optional[LatestHeadlinesGetRequestClusteringVariable] - Specifies which part of the article to use for determining similarity when clustering. + word_count_max : typing.Optional[WordCountMax] - Possible values are: - - `content`: Uses the full article content (default). - - `title`: Uses only the article title. - - `summary`: Uses the article summary. + page : typing.Optional[Page] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + page_size : typing.Optional[PageSize] - clustering_threshold : typing.Optional[float] - Sets the similarity threshold for grouping articles into clusters. A lower value creates more inclusive clusters, while a higher value requires greater similarity between articles. + clustering_enabled : typing.Optional[ClusteringEnabled] - Examples: - - `0.3`: Results in larger, more diverse clusters. - - `0.6`: Balances cluster size and article similarity (default). - - `0.9`: Creates smaller, tightly related clusters. + clustering_variable : typing.Optional[ClusteringVariable] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + clustering_threshold : typing.Optional[ClusteringThreshold] include_translation_fields : typing.Optional[IncludeTranslationFields] @@ -268,115 +217,29 @@ def get( has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. + theme : typing.Optional[Theme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + not_theme : typing.Optional[NotTheme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + org_entity_name : typing.Optional[OrgEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + per_entity_name : typing.Optional[PerEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + loc_entity_name : typing.Optional[LocEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + misc_entity_name : typing.Optional[MiscEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -384,8 +247,6 @@ def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -393,8 +254,6 @@ def get( iab_tags : typing.Optional[str] Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - Example: `"Business, Events"` - **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -402,8 +261,6 @@ def get( not_iab_tags : typing.Optional[str] Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - Example: `"Agriculture, Metals"` - **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -411,14 +268,13 @@ def get( custom_tags : typing.Optional[str] Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. + - `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` + Example: `custom_tags.industry="Manufacturing,Logistics"` - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). + To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -426,7 +282,7 @@ def get( Returns ------- LatestHeadlinesGetResponse - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. Examples -------- @@ -435,23 +291,56 @@ def get( client = NewscatcherApi( api_key="YOUR_API_KEY", ) - client.latestheadlines.get( - predefined_sources="top 100 US, top 5 GB", + client.latest_headlines.latest_headlines_get( + when="7d", + by_parse_date=True, + lang="en,es", + not_lang="fr,de", + countries="US,CA", + not_countries="UK,FR", + predefined_sources="top 50 US, top 20 GB", + sources="nytimes.com,finance.yahoo.com", + not_sources="cnn.com,wsj.com", + not_author_name="John Doe, Jane Doe", + ranked_only=True, + is_headline=True, + is_opinion=True, + is_paid_content=False, + parent_url="wsj.com/politics,wsj.com/tech", + all_links="https://aiindex.stanford.edu/report,https://www.stateof.ai", + all_domain_links="who.int,nih.gov", + all_links_text="Nvidia,Tesla", + word_count_min=300, + word_count_max=1000, + page=2, + page_size=50, + clustering_enabled=True, + clustering_threshold=0.6, include_translation_fields=True, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", + theme="Finance,Tech", + not_theme="Crime,Sports", + org_entity_name='"Apple Inc" OR Microsoft', + per_entity_name='"Elon Musk" OR "Jeff Bezos"', + loc_entity_name='"San Francisco" OR "New York City"', + misc_entity_name='AWS OR "Microsoft Azure"', + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, iptc_tags="20000199,20000209", not_iptc_tags="20000205,20000209", iab_tags="Business,Events", not_iab_tags="Agriculture,Metals", - custom_tags="Tag1,Tag2,Tag3", + custom_tags="Tag1,Tag2", + robots_compliant=True, ) """ - _response = self._raw_client.get( + _response = self._raw_client.latest_headlines_get( when=when, by_parse_date=by_parse_date, + sort_by=sort_by, lang=lang, not_lang=not_lang, countries=countries, @@ -467,6 +356,7 @@ def get( parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, word_count_min=word_count_min, word_count_max=word_count_max, page=page, @@ -497,7 +387,7 @@ def get( ) return _response.data - def post( + def latest_headlines_post( self, *, when: typing.Optional[When] = OMIT, @@ -517,6 +407,7 @@ def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, word_count_min: typing.Optional[WordCountMin] = OMIT, word_count_max: typing.Optional[WordCountMax] = OMIT, page: typing.Optional[Page] = OMIT, @@ -584,6 +475,8 @@ def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + word_count_min : typing.Optional[WordCountMin] word_count_max : typing.Optional[WordCountMax] @@ -642,7 +535,7 @@ def post( Returns ------- LatestHeadlinesPostResponse - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. Examples -------- @@ -651,14 +544,12 @@ def post( client = NewscatcherApi( api_key="YOUR_API_KEY", ) - client.latestheadlines.post( - lang="en", - predefined_sources=["top 50 US", "top 20 GB"], - is_opinion=False, - page_size=10, + client.latest_headlines.latest_headlines_post( + when="7d", + page_size=1, ) """ - _response = self._raw_client.post( + _response = self._raw_client.latest_headlines_post( when=when, by_parse_date=by_parse_date, lang=lang, @@ -676,6 +567,7 @@ def post( parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, word_count_min=word_count_min, word_count_max=word_count_max, page=page, @@ -707,26 +599,27 @@ def post( return _response.data -class AsyncLatestheadlinesClient: +class AsyncLatestHeadlinesClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawLatestheadlinesClient(client_wrapper=client_wrapper) + self._raw_client = AsyncRawLatestHeadlinesClient(client_wrapper=client_wrapper) @property - def with_raw_response(self) -> AsyncRawLatestheadlinesClient: + def with_raw_response(self) -> AsyncRawLatestHeadlinesClient: """ Retrieves a raw implementation of this client that returns raw responses. Returns ------- - AsyncRawLatestheadlinesClient + AsyncRawLatestHeadlinesClient """ return self._raw_client - async def get( + async def latest_headlines_get( self, *, - when: typing.Optional[str] = None, - by_parse_date: typing.Optional[bool] = None, + when: typing.Optional[When] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, lang: typing.Optional[str] = None, not_lang: typing.Optional[str] = None, countries: typing.Optional[str] = None, @@ -735,39 +628,40 @@ async def get( sources: typing.Optional[str] = None, not_sources: typing.Optional[str] = None, not_author_name: typing.Optional[str] = None, - ranked_only: typing.Optional[bool] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + ranked_only: typing.Optional[RankedOnly] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - clustering_enabled: typing.Optional[bool] = None, - clustering_variable: typing.Optional[LatestHeadlinesGetRequestClusteringVariable] = None, - clustering_threshold: typing.Optional[float] = None, + all_links_text: typing.Optional[str] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, + clustering_enabled: typing.Optional[ClusteringEnabled] = None, + clustering_variable: typing.Optional[ClusteringVariable] = None, + clustering_threshold: typing.Optional[ClusteringThreshold] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, iab_tags: typing.Optional[str] = None, not_iab_tags: typing.Optional[str] = None, custom_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> LatestHeadlinesGetResponse: """ @@ -775,143 +669,89 @@ async def get( Parameters ---------- - when : typing.Optional[str] - The time period for which you want to get the latest headlines. + when : typing.Optional[When] - Format examples: - - `7d`: Last seven days - - `30d`: Last 30 days - - `1h`: Last hour - - `24h`: Last 24 hours + by_parse_date : typing.Optional[ByParseDate] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + sort_by : typing.Optional[SortBy] lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). - - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. - - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. - - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - page_size : typing.Optional[int] - The number of articles to return per page. + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - clustering_enabled : typing.Optional[bool] - Determines whether to group similar articles into clusters. If true, the API returns clustered results. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + word_count_min : typing.Optional[WordCountMin] - clustering_variable : typing.Optional[LatestHeadlinesGetRequestClusteringVariable] - Specifies which part of the article to use for determining similarity when clustering. + word_count_max : typing.Optional[WordCountMax] - Possible values are: - - `content`: Uses the full article content (default). - - `title`: Uses only the article title. - - `summary`: Uses the article summary. + page : typing.Optional[Page] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + page_size : typing.Optional[PageSize] - clustering_threshold : typing.Optional[float] - Sets the similarity threshold for grouping articles into clusters. A lower value creates more inclusive clusters, while a higher value requires greater similarity between articles. + clustering_enabled : typing.Optional[ClusteringEnabled] - Examples: - - `0.3`: Results in larger, more diverse clusters. - - `0.6`: Balances cluster size and article similarity (default). - - `0.9`: Creates smaller, tightly related clusters. + clustering_variable : typing.Optional[ClusteringVariable] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + clustering_threshold : typing.Optional[ClusteringThreshold] include_translation_fields : typing.Optional[IncludeTranslationFields] @@ -919,115 +759,29 @@ async def get( has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. + theme : typing.Optional[Theme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + not_theme : typing.Optional[NotTheme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + org_entity_name : typing.Optional[OrgEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + per_entity_name : typing.Optional[PerEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + loc_entity_name : typing.Optional[LocEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + misc_entity_name : typing.Optional[MiscEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1035,8 +789,6 @@ async def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1044,8 +796,6 @@ async def get( iab_tags : typing.Optional[str] Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - Example: `"Business, Events"` - **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -1053,8 +803,6 @@ async def get( not_iab_tags : typing.Optional[str] Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - Example: `"Agriculture, Metals"` - **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -1062,14 +810,13 @@ async def get( custom_tags : typing.Optional[str] Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. + - `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` + Example: `custom_tags.industry="Manufacturing,Logistics"` - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). + To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1077,7 +824,7 @@ async def get( Returns ------- LatestHeadlinesGetResponse - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. Examples -------- @@ -1091,26 +838,59 @@ async def get( async def main() -> None: - await client.latestheadlines.get( - predefined_sources="top 100 US, top 5 GB", + await client.latest_headlines.latest_headlines_get( + when="7d", + by_parse_date=True, + lang="en,es", + not_lang="fr,de", + countries="US,CA", + not_countries="UK,FR", + predefined_sources="top 50 US, top 20 GB", + sources="nytimes.com,finance.yahoo.com", + not_sources="cnn.com,wsj.com", + not_author_name="John Doe, Jane Doe", + ranked_only=True, + is_headline=True, + is_opinion=True, + is_paid_content=False, + parent_url="wsj.com/politics,wsj.com/tech", + all_links="https://aiindex.stanford.edu/report,https://www.stateof.ai", + all_domain_links="who.int,nih.gov", + all_links_text="Nvidia,Tesla", + word_count_min=300, + word_count_max=1000, + page=2, + page_size=50, + clustering_enabled=True, + clustering_threshold=0.6, include_translation_fields=True, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", + theme="Finance,Tech", + not_theme="Crime,Sports", + org_entity_name='"Apple Inc" OR Microsoft', + per_entity_name='"Elon Musk" OR "Jeff Bezos"', + loc_entity_name='"San Francisco" OR "New York City"', + misc_entity_name='AWS OR "Microsoft Azure"', + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, iptc_tags="20000199,20000209", not_iptc_tags="20000205,20000209", iab_tags="Business,Events", not_iab_tags="Agriculture,Metals", - custom_tags="Tag1,Tag2,Tag3", + custom_tags="Tag1,Tag2", + robots_compliant=True, ) asyncio.run(main()) """ - _response = await self._raw_client.get( + _response = await self._raw_client.latest_headlines_get( when=when, by_parse_date=by_parse_date, + sort_by=sort_by, lang=lang, not_lang=not_lang, countries=countries, @@ -1126,6 +906,7 @@ async def main() -> None: parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, word_count_min=word_count_min, word_count_max=word_count_max, page=page, @@ -1156,7 +937,7 @@ async def main() -> None: ) return _response.data - async def post( + async def latest_headlines_post( self, *, when: typing.Optional[When] = OMIT, @@ -1176,6 +957,7 @@ async def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, word_count_min: typing.Optional[WordCountMin] = OMIT, word_count_max: typing.Optional[WordCountMax] = OMIT, page: typing.Optional[Page] = OMIT, @@ -1243,6 +1025,8 @@ async def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + word_count_min : typing.Optional[WordCountMin] word_count_max : typing.Optional[WordCountMax] @@ -1301,7 +1085,7 @@ async def post( Returns ------- LatestHeadlinesPostResponse - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. Examples -------- @@ -1315,17 +1099,15 @@ async def post( async def main() -> None: - await client.latestheadlines.post( - lang="en", - predefined_sources=["top 50 US", "top 20 GB"], - is_opinion=False, - page_size=10, + await client.latest_headlines.latest_headlines_post( + when="7d", + page_size=1, ) asyncio.run(main()) """ - _response = await self._raw_client.post( + _response = await self._raw_client.latest_headlines_post( when=when, by_parse_date=by_parse_date, lang=lang, @@ -1343,6 +1125,7 @@ async def main() -> None: parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, word_count_min=word_count_min, word_count_max=word_count_max, page=page, diff --git a/src/newscatcher/latestheadlines/raw_client.py b/src/newscatcher/latest_headlines/raw_client.py similarity index 67% rename from src/newscatcher/latestheadlines/raw_client.py rename to src/newscatcher/latest_headlines/raw_client.py index 7eeac74..223b7d3 100644 --- a/src/newscatcher/latestheadlines/raw_client.py +++ b/src/newscatcher/latest_headlines/raw_client.py @@ -6,6 +6,7 @@ from ..core.api_error import ApiError from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.parse_error import ParsingError from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..core.serialization import convert_and_respect_annotation_metadata @@ -18,6 +19,7 @@ from ..errors.unprocessable_entity_error import UnprocessableEntityError from ..types.all_domain_links import AllDomainLinks from ..types.all_links import AllLinks +from ..types.all_links_text import AllLinksText from ..types.by_parse_date import ByParseDate from ..types.clustering_enabled import ClusteringEnabled from ..types.clustering_threshold import ClusteringThreshold @@ -53,6 +55,7 @@ from ..types.predefined_sources import PredefinedSources from ..types.ranked_only import RankedOnly from ..types.robots_compliant import RobotsCompliant +from ..types.sort_by import SortBy from ..types.sources import Sources from ..types.theme import Theme from ..types.title_sentiment_max import TitleSentimentMax @@ -60,23 +63,24 @@ from ..types.when import When from ..types.word_count_max import WordCountMax from ..types.word_count_min import WordCountMin -from .types.latest_headlines_get_request_clustering_variable import LatestHeadlinesGetRequestClusteringVariable from .types.latest_headlines_get_response import LatestHeadlinesGetResponse from .types.latest_headlines_post_response import LatestHeadlinesPostResponse +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) -class RawLatestheadlinesClient: +class RawLatestHeadlinesClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def get( + def latest_headlines_get( self, *, - when: typing.Optional[str] = None, - by_parse_date: typing.Optional[bool] = None, + when: typing.Optional[When] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, lang: typing.Optional[str] = None, not_lang: typing.Optional[str] = None, countries: typing.Optional[str] = None, @@ -85,39 +89,40 @@ def get( sources: typing.Optional[str] = None, not_sources: typing.Optional[str] = None, not_author_name: typing.Optional[str] = None, - ranked_only: typing.Optional[bool] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + ranked_only: typing.Optional[RankedOnly] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - clustering_enabled: typing.Optional[bool] = None, - clustering_variable: typing.Optional[LatestHeadlinesGetRequestClusteringVariable] = None, - clustering_threshold: typing.Optional[float] = None, + all_links_text: typing.Optional[str] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, + clustering_enabled: typing.Optional[ClusteringEnabled] = None, + clustering_variable: typing.Optional[ClusteringVariable] = None, + clustering_threshold: typing.Optional[ClusteringThreshold] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, iab_tags: typing.Optional[str] = None, not_iab_tags: typing.Optional[str] = None, custom_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[LatestHeadlinesGetResponse]: """ @@ -125,143 +130,89 @@ def get( Parameters ---------- - when : typing.Optional[str] - The time period for which you want to get the latest headlines. + when : typing.Optional[When] - Format examples: - - `7d`: Last seven days - - `30d`: Last 30 days - - `1h`: Last hour - - `24h`: Last 24 hours + by_parse_date : typing.Optional[ByParseDate] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + sort_by : typing.Optional[SortBy] lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). - - page_size : typing.Optional[int] - The number of articles to return per page. - - clustering_enabled : typing.Optional[bool] - Determines whether to group similar articles into clusters. If true, the API returns clustered results. - - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + word_count_min : typing.Optional[WordCountMin] - clustering_variable : typing.Optional[LatestHeadlinesGetRequestClusteringVariable] - Specifies which part of the article to use for determining similarity when clustering. + word_count_max : typing.Optional[WordCountMax] - Possible values are: - - `content`: Uses the full article content (default). - - `title`: Uses only the article title. - - `summary`: Uses the article summary. + page : typing.Optional[Page] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + page_size : typing.Optional[PageSize] - clustering_threshold : typing.Optional[float] - Sets the similarity threshold for grouping articles into clusters. A lower value creates more inclusive clusters, while a higher value requires greater similarity between articles. + clustering_enabled : typing.Optional[ClusteringEnabled] - Examples: - - `0.3`: Results in larger, more diverse clusters. - - `0.6`: Balances cluster size and article similarity (default). - - `0.9`: Creates smaller, tightly related clusters. + clustering_variable : typing.Optional[ClusteringVariable] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + clustering_threshold : typing.Optional[ClusteringThreshold] include_translation_fields : typing.Optional[IncludeTranslationFields] @@ -269,115 +220,29 @@ def get( has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. + theme : typing.Optional[Theme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + not_theme : typing.Optional[NotTheme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + org_entity_name : typing.Optional[OrgEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + per_entity_name : typing.Optional[PerEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + loc_entity_name : typing.Optional[LocEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + misc_entity_name : typing.Optional[MiscEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -385,8 +250,6 @@ def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -394,8 +257,6 @@ def get( iab_tags : typing.Optional[str] Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - Example: `"Business, Events"` - **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -403,8 +264,6 @@ def get( not_iab_tags : typing.Optional[str] Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - Example: `"Agriculture, Metals"` - **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -412,14 +271,13 @@ def get( custom_tags : typing.Optional[str] Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. + - `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` + Example: `custom_tags.industry="Manufacturing,Logistics"` - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). + To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -427,7 +285,7 @@ def get( Returns ------- HttpResponse[LatestHeadlinesGetResponse] - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. """ _response = self._client_wrapper.httpx_client.request( "api/latest_headlines", @@ -435,6 +293,7 @@ def get( params={ "when": when, "by_parse_date": by_parse_date, + "sort_by": sort_by, "lang": lang, "not_lang": not_lang, "countries": countries, @@ -450,6 +309,7 @@ def get( "parent_url": parent_url, "all_links": all_links, "all_domain_links": all_domain_links, + "all_links_text": all_links_text, "word_count_min": word_count_min, "word_count_max": word_count_max, "page": page, @@ -569,9 +429,13 @@ def get( _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 post( + def latest_headlines_post( self, *, when: typing.Optional[When] = OMIT, @@ -591,6 +455,7 @@ def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, word_count_min: typing.Optional[WordCountMin] = OMIT, word_count_max: typing.Optional[WordCountMax] = OMIT, page: typing.Optional[Page] = OMIT, @@ -658,6 +523,8 @@ def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + word_count_min : typing.Optional[WordCountMin] word_count_max : typing.Optional[WordCountMax] @@ -716,7 +583,7 @@ def post( Returns ------- HttpResponse[LatestHeadlinesPostResponse] - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. """ _response = self._client_wrapper.httpx_client.request( "api/latest_headlines", @@ -759,6 +626,9 @@ def post( "all_domain_links": convert_and_respect_annotation_metadata( object_=all_domain_links, annotation=AllDomainLinks, direction="write" ), + "all_links_text": convert_and_respect_annotation_metadata( + object_=all_links_text, annotation=AllLinksText, direction="write" + ), "word_count_min": word_count_min, "word_count_max": word_count_max, "page": page, @@ -769,10 +639,8 @@ def post( "include_translation_fields": include_translation_fields, "include_nlp_data": include_nlp_data, "has_nlp": has_nlp, - "theme": convert_and_respect_annotation_metadata(object_=theme, annotation=Theme, direction="write"), - "not_theme": convert_and_respect_annotation_metadata( - object_=not_theme, annotation=NotTheme, direction="write" - ), + "theme": theme, + "not_theme": not_theme, "ORG_entity_name": org_entity_name, "PER_entity_name": per_entity_name, "LOC_entity_name": loc_entity_name, @@ -894,18 +762,23 @@ def post( _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) -class AsyncRawLatestheadlinesClient: +class AsyncRawLatestHeadlinesClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def get( + async def latest_headlines_get( self, *, - when: typing.Optional[str] = None, - by_parse_date: typing.Optional[bool] = None, + when: typing.Optional[When] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, lang: typing.Optional[str] = None, not_lang: typing.Optional[str] = None, countries: typing.Optional[str] = None, @@ -914,39 +787,40 @@ async def get( sources: typing.Optional[str] = None, not_sources: typing.Optional[str] = None, not_author_name: typing.Optional[str] = None, - ranked_only: typing.Optional[bool] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + ranked_only: typing.Optional[RankedOnly] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - clustering_enabled: typing.Optional[bool] = None, - clustering_variable: typing.Optional[LatestHeadlinesGetRequestClusteringVariable] = None, - clustering_threshold: typing.Optional[float] = None, + all_links_text: typing.Optional[str] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, + clustering_enabled: typing.Optional[ClusteringEnabled] = None, + clustering_variable: typing.Optional[ClusteringVariable] = None, + clustering_threshold: typing.Optional[ClusteringThreshold] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, iab_tags: typing.Optional[str] = None, not_iab_tags: typing.Optional[str] = None, custom_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[LatestHeadlinesGetResponse]: """ @@ -954,143 +828,89 @@ async def get( Parameters ---------- - when : typing.Optional[str] - The time period for which you want to get the latest headlines. + when : typing.Optional[When] - Format examples: - - `7d`: Last seven days - - `30d`: Last 30 days - - `1h`: Last hour - - `24h`: Last 24 hours + by_parse_date : typing.Optional[ByParseDate] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + sort_by : typing.Optional[SortBy] lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). - - page_size : typing.Optional[int] - The number of articles to return per page. - - clustering_enabled : typing.Optional[bool] - Determines whether to group similar articles into clusters. If true, the API returns clustered results. - - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + word_count_min : typing.Optional[WordCountMin] - clustering_variable : typing.Optional[LatestHeadlinesGetRequestClusteringVariable] - Specifies which part of the article to use for determining similarity when clustering. + word_count_max : typing.Optional[WordCountMax] - Possible values are: - - `content`: Uses the full article content (default). - - `title`: Uses only the article title. - - `summary`: Uses the article summary. + page : typing.Optional[Page] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + page_size : typing.Optional[PageSize] - clustering_threshold : typing.Optional[float] - Sets the similarity threshold for grouping articles into clusters. A lower value creates more inclusive clusters, while a higher value requires greater similarity between articles. + clustering_enabled : typing.Optional[ClusteringEnabled] - Examples: - - `0.3`: Results in larger, more diverse clusters. - - `0.6`: Balances cluster size and article similarity (default). - - `0.9`: Creates smaller, tightly related clusters. + clustering_variable : typing.Optional[ClusteringVariable] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + clustering_threshold : typing.Optional[ClusteringThreshold] include_translation_fields : typing.Optional[IncludeTranslationFields] @@ -1098,115 +918,29 @@ async def get( has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. + theme : typing.Optional[Theme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + not_theme : typing.Optional[NotTheme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + org_entity_name : typing.Optional[OrgEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + per_entity_name : typing.Optional[PerEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + loc_entity_name : typing.Optional[LocEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + misc_entity_name : typing.Optional[MiscEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1214,8 +948,6 @@ async def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1223,8 +955,6 @@ async def get( iab_tags : typing.Optional[str] Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - Example: `"Business, Events"` - **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -1232,8 +962,6 @@ async def get( not_iab_tags : typing.Optional[str] Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - Example: `"Agriculture, Metals"` - **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -1241,14 +969,13 @@ async def get( custom_tags : typing.Optional[str] Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. + - `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` + Example: `custom_tags.industry="Manufacturing,Logistics"` - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). + To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1256,7 +983,7 @@ async def get( Returns ------- AsyncHttpResponse[LatestHeadlinesGetResponse] - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. """ _response = await self._client_wrapper.httpx_client.request( "api/latest_headlines", @@ -1264,6 +991,7 @@ async def get( params={ "when": when, "by_parse_date": by_parse_date, + "sort_by": sort_by, "lang": lang, "not_lang": not_lang, "countries": countries, @@ -1279,6 +1007,7 @@ async def get( "parent_url": parent_url, "all_links": all_links, "all_domain_links": all_domain_links, + "all_links_text": all_links_text, "word_count_min": word_count_min, "word_count_max": word_count_max, "page": page, @@ -1398,9 +1127,13 @@ async def get( _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 post( + async def latest_headlines_post( self, *, when: typing.Optional[When] = OMIT, @@ -1420,6 +1153,7 @@ async def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, word_count_min: typing.Optional[WordCountMin] = OMIT, word_count_max: typing.Optional[WordCountMax] = OMIT, page: typing.Optional[Page] = OMIT, @@ -1487,6 +1221,8 @@ async def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + word_count_min : typing.Optional[WordCountMin] word_count_max : typing.Optional[WordCountMax] @@ -1545,7 +1281,7 @@ async def post( Returns ------- AsyncHttpResponse[LatestHeadlinesPostResponse] - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. """ _response = await self._client_wrapper.httpx_client.request( "api/latest_headlines", @@ -1588,6 +1324,9 @@ async def post( "all_domain_links": convert_and_respect_annotation_metadata( object_=all_domain_links, annotation=AllDomainLinks, direction="write" ), + "all_links_text": convert_and_respect_annotation_metadata( + object_=all_links_text, annotation=AllLinksText, direction="write" + ), "word_count_min": word_count_min, "word_count_max": word_count_max, "page": page, @@ -1598,10 +1337,8 @@ async def post( "include_translation_fields": include_translation_fields, "include_nlp_data": include_nlp_data, "has_nlp": has_nlp, - "theme": convert_and_respect_annotation_metadata(object_=theme, annotation=Theme, direction="write"), - "not_theme": convert_and_respect_annotation_metadata( - object_=not_theme, annotation=NotTheme, direction="write" - ), + "theme": theme, + "not_theme": not_theme, "ORG_entity_name": org_entity_name, "PER_entity_name": per_entity_name, "LOC_entity_name": loc_entity_name, @@ -1723,4 +1460,8 @@ async def post( _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) diff --git a/src/newscatcher/latest_headlines/types/__init__.py b/src/newscatcher/latest_headlines/types/__init__.py new file mode 100644 index 0000000..10e8792 --- /dev/null +++ b/src/newscatcher/latest_headlines/types/__init__.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .latest_headlines_get_response import LatestHeadlinesGetResponse + from .latest_headlines_post_response import LatestHeadlinesPostResponse +_dynamic_imports: typing.Dict[str, str] = { + "LatestHeadlinesGetResponse": ".latest_headlines_get_response", + "LatestHeadlinesPostResponse": ".latest_headlines_post_response", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["LatestHeadlinesGetResponse", "LatestHeadlinesPostResponse"] diff --git a/src/newscatcher/latestheadlines/types/latest_headlines_get_response.py b/src/newscatcher/latest_headlines/types/latest_headlines_get_response.py similarity index 100% rename from src/newscatcher/latestheadlines/types/latest_headlines_get_response.py rename to src/newscatcher/latest_headlines/types/latest_headlines_get_response.py diff --git a/src/newscatcher/latestheadlines/types/latest_headlines_post_response.py b/src/newscatcher/latest_headlines/types/latest_headlines_post_response.py similarity index 100% rename from src/newscatcher/latestheadlines/types/latest_headlines_post_response.py rename to src/newscatcher/latest_headlines/types/latest_headlines_post_response.py diff --git a/src/newscatcher/latestheadlines/__init__.py b/src/newscatcher/latestheadlines/__init__.py deleted file mode 100644 index 79cd966..0000000 --- a/src/newscatcher/latestheadlines/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -from .types import LatestHeadlinesGetRequestClusteringVariable, LatestHeadlinesGetResponse, LatestHeadlinesPostResponse - -__all__ = ["LatestHeadlinesGetRequestClusteringVariable", "LatestHeadlinesGetResponse", "LatestHeadlinesPostResponse"] diff --git a/src/newscatcher/latestheadlines/types/__init__.py b/src/newscatcher/latestheadlines/types/__init__.py deleted file mode 100644 index a3c6699..0000000 --- a/src/newscatcher/latestheadlines/types/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -from .latest_headlines_get_request_clustering_variable import LatestHeadlinesGetRequestClusteringVariable -from .latest_headlines_get_response import LatestHeadlinesGetResponse -from .latest_headlines_post_response import LatestHeadlinesPostResponse - -__all__ = ["LatestHeadlinesGetRequestClusteringVariable", "LatestHeadlinesGetResponse", "LatestHeadlinesPostResponse"] diff --git a/src/newscatcher/latestheadlines/types/latest_headlines_get_request_clustering_variable.py b/src/newscatcher/latestheadlines/types/latest_headlines_get_request_clustering_variable.py deleted file mode 100644 index 2d68a10..0000000 --- a/src/newscatcher/latestheadlines/types/latest_headlines_get_request_clustering_variable.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -LatestHeadlinesGetRequestClusteringVariable = typing.Union[typing.Literal["content", "title", "summary"], typing.Any] diff --git a/src/newscatcher/search/__init__.py b/src/newscatcher/search/__init__.py index b2bfd44..3c584ee 100644 --- a/src/newscatcher/search/__init__.py +++ b/src/newscatcher/search/__init__.py @@ -2,20 +2,33 @@ # isort: skip_file -from .types import ( - SearchGetRequestClusteringVariable, - SearchGetRequestNewsDomainType, - SearchGetRequestPublishedDatePrecision, - SearchGetRequestSortBy, - SearchGetResponse, - SearchPostResponse, -) - -__all__ = [ - "SearchGetRequestClusteringVariable", - "SearchGetRequestNewsDomainType", - "SearchGetRequestPublishedDatePrecision", - "SearchGetRequestSortBy", - "SearchGetResponse", - "SearchPostResponse", -] +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import SearchGetResponse, SearchPostResponse +_dynamic_imports: typing.Dict[str, str] = {"SearchGetResponse": ".types", "SearchPostResponse": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["SearchGetResponse", "SearchPostResponse"] diff --git a/src/newscatcher/search/client.py b/src/newscatcher/search/client.py index 40ea215..bb7082c 100644 --- a/src/newscatcher/search/client.py +++ b/src/newscatcher/search/client.py @@ -1,6 +1,5 @@ # This file was auto-generated by Fern from our API Definition. -import datetime as dt import typing from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper @@ -8,6 +7,7 @@ from ..types.additional_domain_info import AdditionalDomainInfo from ..types.all_domain_links import AllDomainLinks from ..types.all_links import AllLinks +from ..types.all_links_text import AllLinksText from ..types.by_parse_date import ByParseDate from ..types.clustering_enabled import ClusteringEnabled from ..types.clustering_threshold import ClusteringThreshold @@ -62,10 +62,6 @@ from ..types.word_count_max import WordCountMax from ..types.word_count_min import WordCountMin from .raw_client import AsyncRawSearchClient, RawSearchClient -from .types.search_get_request_clustering_variable import SearchGetRequestClusteringVariable -from .types.search_get_request_news_domain_type import SearchGetRequestNewsDomainType -from .types.search_get_request_published_date_precision import SearchGetRequestPublishedDatePrecision -from .types.search_get_request_sort_by import SearchGetRequestSortBy from .types.search_get_response import SearchGetResponse from .types.search_post_response import SearchPostResponse @@ -91,7 +87,7 @@ def with_raw_response(self) -> RawSearchClient: def get( self, *, - q: str, + q: Q, search_in: typing.Optional[SearchIn] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, predefined_sources: typing.Optional[str] = None, @@ -103,68 +99,59 @@ def get( countries: typing.Optional[str] = None, not_countries: typing.Optional[str] = None, not_author_name: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - published_date_precision: typing.Optional[SearchGetRequestPublishedDatePrecision] = None, - by_parse_date: typing.Optional[bool] = None, - sort_by: typing.Optional[SearchGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + from_: typing.Optional[From] = None, + to: typing.Optional[To] = None, + published_date_precision: typing.Optional[PublishedDatePrecision] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - additional_domain_info: typing.Optional[bool] = None, - is_news_domain: typing.Optional[bool] = None, - news_domain_type: typing.Optional[SearchGetRequestNewsDomainType] = None, + all_links_text: typing.Optional[str] = None, + additional_domain_info: typing.Optional[AdditionalDomainInfo] = None, + is_news_domain: typing.Optional[IsNewsDomain] = None, + news_domain_type: typing.Optional[NewsDomainType] = None, news_type: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - clustering_enabled: typing.Optional[bool] = None, - clustering_variable: typing.Optional[SearchGetRequestClusteringVariable] = None, - clustering_threshold: typing.Optional[float] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, + clustering_enabled: typing.Optional[ClusteringEnabled] = None, + clustering_variable: typing.Optional[ClusteringVariable] = None, + clustering_threshold: typing.Optional[ClusteringThreshold] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, iab_tags: typing.Optional[str] = None, not_iab_tags: typing.Optional[str] = None, custom_tags: typing.Optional[str] = None, - exclude_duplicates: typing.Optional[bool] = None, - robots_compliant: typing.Optional[bool] = None, + exclude_duplicates: typing.Optional[ExcludeDuplicates] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> SearchGetResponse: """ - Searches for articles based on specified criteria such as keyword, language, country, source, and more. + Searches for articles based on specified criteria such as keywords, language, country, source, and more. Parameters ---------- - q : str - The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - - - For an exact match, use double quotes. For example, `"technology news"`. - - Use `*` to search for any keyword. - - Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. - - Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - - For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). + q : Q search_in : typing.Optional[SearchIn] @@ -173,318 +160,138 @@ def get( predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. source_name : typing.Optional[str] Word or phrase to search within the source names. To specify multiple values, use a comma-separated string. - Example: `"sport, tech"` - **Note**: The search doesn't require an exact match and returns sources containing the specified terms in their names. You can use any word or phrase, like `"sport"` or `"new york times"`. For example, `"sport"` returns sources such as `"Motorsport"`, `"Dot Esport"`, and `"Tuttosport"`. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` + from_ : typing.Optional[From] - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. + to : typing.Optional[To] - published_date_precision : typing.Optional[SearchGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. + published_date_precision : typing.Optional[PublishedDatePrecision] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + by_parse_date : typing.Optional[ByParseDate] - sort_by : typing.Optional[SearchGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. + sort_by : typing.Optional[SortBy] - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). - - additional_domain_info : typing.Optional[bool] - If true, includes additional domain information in the response for each article: - - `is_news_domain`: Boolean indicating if the source is a news domain. - - `news_domain_type`: Type of news domain (e.g., `"Original Content"`). - - `news_type`: Category of news (e.g., `"News and Blogs"`). - - is_news_domain : typing.Optional[bool] - If true, filters results to include only news domains. - - news_domain_type : typing.Optional[SearchGetRequestNewsDomainType] - Filters results based on the news domain type. Possible values are: - - `Original Content`: Sources that produce their own content. - - `Aggregator`: Sources that collect content from various other sources. - - `Press Releases`: Sources primarily publishing press releases. - - `Republisher`: Sources that republish content from other sources. - - `Other`: Sources that don't fit into main categories. - - news_type : typing.Optional[str] - Filters results based on the news type. Multiple types can be specified using a comma-separated string. + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - Example: `"General News Outlets,Tech News and Updates"` + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - For a complete list of available news types, see [Enumerated parameters > News type](/docs/v3/api-reference/overview/enumerated-parameters#news-type-news-type). + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. + additional_domain_info : typing.Optional[AdditionalDomainInfo] - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + is_news_domain : typing.Optional[IsNewsDomain] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + news_domain_type : typing.Optional[NewsDomainType] - page_size : typing.Optional[int] - The number of articles to return per page. + news_type : typing.Optional[str] + Filters results based on the news type. Multiple types can be specified using a comma-separated string. - clustering_enabled : typing.Optional[bool] - Determines whether to group similar articles into clusters. If true, the API returns clustered results. + For a complete list of available news types, see [Enumerated parameters > News type](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#news-type-news-type). - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + word_count_min : typing.Optional[WordCountMin] - clustering_variable : typing.Optional[SearchGetRequestClusteringVariable] - Specifies which part of the article to use for determining similarity when clustering. + word_count_max : typing.Optional[WordCountMax] - Possible values are: - - `content`: Uses the full article content (default). - - `title`: Uses only the article title. - - `summary`: Uses the article summary. + page : typing.Optional[Page] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + page_size : typing.Optional[PageSize] - clustering_threshold : typing.Optional[float] - Sets the similarity threshold for grouping articles into clusters. A lower value creates more inclusive clusters, while a higher value requires greater similarity between articles. + clustering_enabled : typing.Optional[ClusteringEnabled] - Examples: - - `0.3`: Results in larger, more diverse clusters. - - `0.6`: Balances cluster size and article similarity (default). - - `0.9`: Creates smaller, tightly related clusters. + clustering_variable : typing.Optional[ClusteringVariable] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + clustering_threshold : typing.Optional[ClusteringThreshold] include_nlp_data : typing.Optional[IncludeNlpData] has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. + theme : typing.Optional[Theme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + not_theme : typing.Optional[NotTheme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + org_entity_name : typing.Optional[OrgEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + per_entity_name : typing.Optional[PerEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + loc_entity_name : typing.Optional[LocEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + misc_entity_name : typing.Optional[MiscEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -492,8 +299,6 @@ def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -501,8 +306,6 @@ def get( iab_tags : typing.Optional[str] Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - Example: `"Business, Events"` - **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -510,8 +313,6 @@ def get( not_iab_tags : typing.Optional[str] Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - Example: `"Agriculture, Metals"` - **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -519,19 +320,15 @@ def get( custom_tags : typing.Optional[str] Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. - - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` + - `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). + Example: `custom_tags.industry="Manufacturing,Logistics"` - exclude_duplicates : typing.Optional[bool] - If true, excludes duplicate and highly similar articles from the search results. If false, returns all relevant articles, including duplicates. + To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). - To learn more, see [Articles deduplication](/docs/v3/documentation/guides-and-concepts/articles-deduplication). + exclude_duplicates : typing.Optional[ExcludeDuplicates] - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -539,7 +336,7 @@ def get( Returns ------- SearchGetResponse - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. Examples -------- @@ -551,25 +348,64 @@ def get( api_key="YOUR_API_KEY", ) client.search.get( - q="technology AND (Apple OR Microsoft) NOT Google", + q='"supply chain" AND Amazon NOT China', search_in="title_content, title_content_translated", include_translation_fields=True, - predefined_sources="top 100 US, top 5 GB", + predefined_sources="top 50 US, top 20 GB", + source_name="sport,tech", + sources="nytimes.com,finance.yahoo.com", + not_sources="cnn.com,wsj.com", + lang="en,es", + not_lang="fr,de", + countries="US,CA", + not_countries="UK,FR", + not_author_name="John Doe, Jane Doe", from_=datetime.datetime.fromisoformat( "2024-07-01 00:00:00+00:00", ), to=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", + "2024-01-01 00:00:00+00:00", ), + published_date_precision="full", + by_parse_date=True, + ranked_only=True, + from_rank=100, + to_rank=100, + is_headline=True, + is_opinion=True, + is_paid_content=False, + parent_url="wsj.com/politics,wsj.com/tech", + all_links="https://aiindex.stanford.edu/report,https://www.stateof.ai", + all_domain_links="who.int,nih.gov", + all_links_text="Nvidia,Tesla", + additional_domain_info=True, + is_news_domain=True, + news_type="General News Outlets,Tech News and Updates", + word_count_min=300, + word_count_max=1000, + page=2, + page_size=50, + clustering_enabled=True, + clustering_threshold=0.6, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", + theme="Finance,Tech", + not_theme="Crime,Sports", + org_entity_name='"Apple Inc" OR Microsoft', + per_entity_name='"Elon Musk" OR "Jeff Bezos"', + loc_entity_name='"San Francisco" OR "New York City"', + misc_entity_name='AWS OR "Microsoft Azure"', + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, iptc_tags="20000199,20000209", not_iptc_tags="20000205,20000209", iab_tags="Business,Events", not_iab_tags="Agriculture,Metals", - custom_tags="Tag1,Tag2,Tag3", + custom_tags="Tag1,Tag2", + exclude_duplicates=True, + robots_compliant=True, ) """ _response = self._raw_client.get( @@ -599,6 +435,7 @@ def get( parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, additional_domain_info=additional_domain_info, is_news_domain=is_news_domain, news_domain_type=news_domain_type, @@ -662,6 +499,7 @@ def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, additional_domain_info: typing.Optional[AdditionalDomainInfo] = OMIT, is_news_domain: typing.Optional[IsNewsDomain] = OMIT, news_domain_type: typing.Optional[NewsDomainType] = OMIT, @@ -695,7 +533,7 @@ def post( request_options: typing.Optional[RequestOptions] = None, ) -> SearchPostResponse: """ - Searches for articles based on specified criteria such as keyword, language, country, source, and more. + Searches for articles based on specified criteria such as keywords, language, country, source, and more. Parameters ---------- @@ -751,6 +589,8 @@ def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + additional_domain_info : typing.Optional[AdditionalDomainInfo] is_news_domain : typing.Optional[IsNewsDomain] @@ -817,29 +657,18 @@ def post( Returns ------- SearchPostResponse - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. Examples -------- - import datetime - from newscatcher import NewscatcherApi client = NewscatcherApi( api_key="YOUR_API_KEY", ) client.search.post( - q="renewable energy", - predefined_sources=["top 50 US"], - lang=["en"], - from_=datetime.datetime.fromisoformat( - "2024-01-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-06-30 00:00:00+00:00", - ), - additional_domain_info=True, - is_news_domain=True, + q='"supply chain" AND Amazon NOT China', + page_size=1, ) """ _response = self._raw_client.post( @@ -869,6 +698,7 @@ def post( parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, additional_domain_info=additional_domain_info, is_news_domain=is_news_domain, news_domain_type=news_domain_type, @@ -922,7 +752,7 @@ def with_raw_response(self) -> AsyncRawSearchClient: async def get( self, *, - q: str, + q: Q, search_in: typing.Optional[SearchIn] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, predefined_sources: typing.Optional[str] = None, @@ -934,68 +764,59 @@ async def get( countries: typing.Optional[str] = None, not_countries: typing.Optional[str] = None, not_author_name: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - published_date_precision: typing.Optional[SearchGetRequestPublishedDatePrecision] = None, - by_parse_date: typing.Optional[bool] = None, - sort_by: typing.Optional[SearchGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + from_: typing.Optional[From] = None, + to: typing.Optional[To] = None, + published_date_precision: typing.Optional[PublishedDatePrecision] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - additional_domain_info: typing.Optional[bool] = None, - is_news_domain: typing.Optional[bool] = None, - news_domain_type: typing.Optional[SearchGetRequestNewsDomainType] = None, + all_links_text: typing.Optional[str] = None, + additional_domain_info: typing.Optional[AdditionalDomainInfo] = None, + is_news_domain: typing.Optional[IsNewsDomain] = None, + news_domain_type: typing.Optional[NewsDomainType] = None, news_type: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - clustering_enabled: typing.Optional[bool] = None, - clustering_variable: typing.Optional[SearchGetRequestClusteringVariable] = None, - clustering_threshold: typing.Optional[float] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, + clustering_enabled: typing.Optional[ClusteringEnabled] = None, + clustering_variable: typing.Optional[ClusteringVariable] = None, + clustering_threshold: typing.Optional[ClusteringThreshold] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, iab_tags: typing.Optional[str] = None, not_iab_tags: typing.Optional[str] = None, custom_tags: typing.Optional[str] = None, - exclude_duplicates: typing.Optional[bool] = None, - robots_compliant: typing.Optional[bool] = None, + exclude_duplicates: typing.Optional[ExcludeDuplicates] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> SearchGetResponse: """ - Searches for articles based on specified criteria such as keyword, language, country, source, and more. + Searches for articles based on specified criteria such as keywords, language, country, source, and more. Parameters ---------- - q : str - The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - - - For an exact match, use double quotes. For example, `"technology news"`. - - Use `*` to search for any keyword. - - Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. - - Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - - For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). + q : Q search_in : typing.Optional[SearchIn] @@ -1004,318 +825,138 @@ async def get( predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. source_name : typing.Optional[str] Word or phrase to search within the source names. To specify multiple values, use a comma-separated string. - Example: `"sport, tech"` - **Note**: The search doesn't require an exact match and returns sources containing the specified terms in their names. You can use any word or phrase, like `"sport"` or `"new york times"`. For example, `"sport"` returns sources such as `"Motorsport"`, `"Dot Esport"`, and `"Tuttosport"`. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` + from_ : typing.Optional[From] - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. + to : typing.Optional[To] - published_date_precision : typing.Optional[SearchGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. + published_date_precision : typing.Optional[PublishedDatePrecision] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + by_parse_date : typing.Optional[ByParseDate] - sort_by : typing.Optional[SearchGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. + sort_by : typing.Optional[SortBy] - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). - - additional_domain_info : typing.Optional[bool] - If true, includes additional domain information in the response for each article: - - `is_news_domain`: Boolean indicating if the source is a news domain. - - `news_domain_type`: Type of news domain (e.g., `"Original Content"`). - - `news_type`: Category of news (e.g., `"News and Blogs"`). - - is_news_domain : typing.Optional[bool] - If true, filters results to include only news domains. - - news_domain_type : typing.Optional[SearchGetRequestNewsDomainType] - Filters results based on the news domain type. Possible values are: - - `Original Content`: Sources that produce their own content. - - `Aggregator`: Sources that collect content from various other sources. - - `Press Releases`: Sources primarily publishing press releases. - - `Republisher`: Sources that republish content from other sources. - - `Other`: Sources that don't fit into main categories. - - news_type : typing.Optional[str] - Filters results based on the news type. Multiple types can be specified using a comma-separated string. + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - Example: `"General News Outlets,Tech News and Updates"` + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - For a complete list of available news types, see [Enumerated parameters > News type](/docs/v3/api-reference/overview/enumerated-parameters#news-type-news-type). + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. + additional_domain_info : typing.Optional[AdditionalDomainInfo] - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + is_news_domain : typing.Optional[IsNewsDomain] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + news_domain_type : typing.Optional[NewsDomainType] - page_size : typing.Optional[int] - The number of articles to return per page. + news_type : typing.Optional[str] + Filters results based on the news type. Multiple types can be specified using a comma-separated string. - clustering_enabled : typing.Optional[bool] - Determines whether to group similar articles into clusters. If true, the API returns clustered results. + For a complete list of available news types, see [Enumerated parameters > News type](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#news-type-news-type). - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + word_count_min : typing.Optional[WordCountMin] - clustering_variable : typing.Optional[SearchGetRequestClusteringVariable] - Specifies which part of the article to use for determining similarity when clustering. + word_count_max : typing.Optional[WordCountMax] - Possible values are: - - `content`: Uses the full article content (default). - - `title`: Uses only the article title. - - `summary`: Uses the article summary. + page : typing.Optional[Page] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + page_size : typing.Optional[PageSize] - clustering_threshold : typing.Optional[float] - Sets the similarity threshold for grouping articles into clusters. A lower value creates more inclusive clusters, while a higher value requires greater similarity between articles. + clustering_enabled : typing.Optional[ClusteringEnabled] - Examples: - - `0.3`: Results in larger, more diverse clusters. - - `0.6`: Balances cluster size and article similarity (default). - - `0.9`: Creates smaller, tightly related clusters. + clustering_variable : typing.Optional[ClusteringVariable] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + clustering_threshold : typing.Optional[ClusteringThreshold] include_nlp_data : typing.Optional[IncludeNlpData] has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. + theme : typing.Optional[Theme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + not_theme : typing.Optional[NotTheme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + org_entity_name : typing.Optional[OrgEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + per_entity_name : typing.Optional[PerEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + loc_entity_name : typing.Optional[LocEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + misc_entity_name : typing.Optional[MiscEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1323,8 +964,6 @@ async def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1332,8 +971,6 @@ async def get( iab_tags : typing.Optional[str] Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - Example: `"Business, Events"` - **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -1341,8 +978,6 @@ async def get( not_iab_tags : typing.Optional[str] Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - Example: `"Agriculture, Metals"` - **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -1350,19 +985,15 @@ async def get( custom_tags : typing.Optional[str] Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. + - `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` + Example: `custom_tags.industry="Manufacturing,Logistics"` - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). + To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). - exclude_duplicates : typing.Optional[bool] - If true, excludes duplicate and highly similar articles from the search results. If false, returns all relevant articles, including duplicates. - - To learn more, see [Articles deduplication](/docs/v3/documentation/guides-and-concepts/articles-deduplication). + exclude_duplicates : typing.Optional[ExcludeDuplicates] - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1370,7 +1001,7 @@ async def get( Returns ------- SearchGetResponse - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. Examples -------- @@ -1386,25 +1017,64 @@ async def get( async def main() -> None: await client.search.get( - q="technology AND (Apple OR Microsoft) NOT Google", + q='"supply chain" AND Amazon NOT China', search_in="title_content, title_content_translated", include_translation_fields=True, - predefined_sources="top 100 US, top 5 GB", + predefined_sources="top 50 US, top 20 GB", + source_name="sport,tech", + sources="nytimes.com,finance.yahoo.com", + not_sources="cnn.com,wsj.com", + lang="en,es", + not_lang="fr,de", + countries="US,CA", + not_countries="UK,FR", + not_author_name="John Doe, Jane Doe", from_=datetime.datetime.fromisoformat( "2024-07-01 00:00:00+00:00", ), to=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", + "2024-01-01 00:00:00+00:00", ), + published_date_precision="full", + by_parse_date=True, + ranked_only=True, + from_rank=100, + to_rank=100, + is_headline=True, + is_opinion=True, + is_paid_content=False, + parent_url="wsj.com/politics,wsj.com/tech", + all_links="https://aiindex.stanford.edu/report,https://www.stateof.ai", + all_domain_links="who.int,nih.gov", + all_links_text="Nvidia,Tesla", + additional_domain_info=True, + is_news_domain=True, + news_type="General News Outlets,Tech News and Updates", + word_count_min=300, + word_count_max=1000, + page=2, + page_size=50, + clustering_enabled=True, + clustering_threshold=0.6, include_nlp_data=True, has_nlp=True, - theme="Business,Finance", - not_theme="Crime", + theme="Finance,Tech", + not_theme="Crime,Sports", + org_entity_name='"Apple Inc" OR Microsoft', + per_entity_name='"Elon Musk" OR "Jeff Bezos"', + loc_entity_name='"San Francisco" OR "New York City"', + misc_entity_name='AWS OR "Microsoft Azure"', + title_sentiment_min=-0.5, + title_sentiment_max=0.5, + content_sentiment_min=-0.5, + content_sentiment_max=0.5, iptc_tags="20000199,20000209", not_iptc_tags="20000205,20000209", iab_tags="Business,Events", not_iab_tags="Agriculture,Metals", - custom_tags="Tag1,Tag2,Tag3", + custom_tags="Tag1,Tag2", + exclude_duplicates=True, + robots_compliant=True, ) @@ -1437,6 +1107,7 @@ async def main() -> None: parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, additional_domain_info=additional_domain_info, is_news_domain=is_news_domain, news_domain_type=news_domain_type, @@ -1500,6 +1171,7 @@ async def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, additional_domain_info: typing.Optional[AdditionalDomainInfo] = OMIT, is_news_domain: typing.Optional[IsNewsDomain] = OMIT, news_domain_type: typing.Optional[NewsDomainType] = OMIT, @@ -1533,7 +1205,7 @@ async def post( request_options: typing.Optional[RequestOptions] = None, ) -> SearchPostResponse: """ - Searches for articles based on specified criteria such as keyword, language, country, source, and more. + Searches for articles based on specified criteria such as keywords, language, country, source, and more. Parameters ---------- @@ -1589,6 +1261,8 @@ async def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + additional_domain_info : typing.Optional[AdditionalDomainInfo] is_news_domain : typing.Optional[IsNewsDomain] @@ -1655,12 +1329,11 @@ async def post( Returns ------- SearchPostResponse - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. Examples -------- import asyncio - import datetime from newscatcher import AsyncNewscatcherApi @@ -1671,17 +1344,8 @@ async def post( async def main() -> None: await client.search.post( - q="renewable energy", - predefined_sources=["top 50 US"], - lang=["en"], - from_=datetime.datetime.fromisoformat( - "2024-01-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-06-30 00:00:00+00:00", - ), - additional_domain_info=True, - is_news_domain=True, + q='"supply chain" AND Amazon NOT China', + page_size=1, ) @@ -1714,6 +1378,7 @@ async def main() -> None: parent_url=parent_url, all_links=all_links, all_domain_links=all_domain_links, + all_links_text=all_links_text, additional_domain_info=additional_domain_info, is_news_domain=is_news_domain, news_domain_type=news_domain_type, diff --git a/src/newscatcher/search/raw_client.py b/src/newscatcher/search/raw_client.py index ec01ca2..2eca68a 100644 --- a/src/newscatcher/search/raw_client.py +++ b/src/newscatcher/search/raw_client.py @@ -1,13 +1,12 @@ # This file was auto-generated by Fern from our API Definition. -import datetime as dt import typing from json.decoder import JSONDecodeError from ..core.api_error import ApiError from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.datetime_utils import serialize_datetime from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.parse_error import ParsingError from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..core.serialization import convert_and_respect_annotation_metadata @@ -21,6 +20,7 @@ from ..types.additional_domain_info import AdditionalDomainInfo from ..types.all_domain_links import AllDomainLinks from ..types.all_links import AllLinks +from ..types.all_links_text import AllLinksText from ..types.by_parse_date import ByParseDate from ..types.clustering_enabled import ClusteringEnabled from ..types.clustering_threshold import ClusteringThreshold @@ -75,12 +75,9 @@ from ..types.to_rank import ToRank from ..types.word_count_max import WordCountMax from ..types.word_count_min import WordCountMin -from .types.search_get_request_clustering_variable import SearchGetRequestClusteringVariable -from .types.search_get_request_news_domain_type import SearchGetRequestNewsDomainType -from .types.search_get_request_published_date_precision import SearchGetRequestPublishedDatePrecision -from .types.search_get_request_sort_by import SearchGetRequestSortBy from .types.search_get_response import SearchGetResponse from .types.search_post_response import SearchPostResponse +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -93,7 +90,7 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): def get( self, *, - q: str, + q: Q, search_in: typing.Optional[SearchIn] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, predefined_sources: typing.Optional[str] = None, @@ -105,68 +102,59 @@ def get( countries: typing.Optional[str] = None, not_countries: typing.Optional[str] = None, not_author_name: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - published_date_precision: typing.Optional[SearchGetRequestPublishedDatePrecision] = None, - by_parse_date: typing.Optional[bool] = None, - sort_by: typing.Optional[SearchGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + from_: typing.Optional[From] = None, + to: typing.Optional[To] = None, + published_date_precision: typing.Optional[PublishedDatePrecision] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - additional_domain_info: typing.Optional[bool] = None, - is_news_domain: typing.Optional[bool] = None, - news_domain_type: typing.Optional[SearchGetRequestNewsDomainType] = None, + all_links_text: typing.Optional[str] = None, + additional_domain_info: typing.Optional[AdditionalDomainInfo] = None, + is_news_domain: typing.Optional[IsNewsDomain] = None, + news_domain_type: typing.Optional[NewsDomainType] = None, news_type: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - clustering_enabled: typing.Optional[bool] = None, - clustering_variable: typing.Optional[SearchGetRequestClusteringVariable] = None, - clustering_threshold: typing.Optional[float] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, + clustering_enabled: typing.Optional[ClusteringEnabled] = None, + clustering_variable: typing.Optional[ClusteringVariable] = None, + clustering_threshold: typing.Optional[ClusteringThreshold] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, iab_tags: typing.Optional[str] = None, not_iab_tags: typing.Optional[str] = None, custom_tags: typing.Optional[str] = None, - exclude_duplicates: typing.Optional[bool] = None, - robots_compliant: typing.Optional[bool] = None, + exclude_duplicates: typing.Optional[ExcludeDuplicates] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[SearchGetResponse]: """ - Searches for articles based on specified criteria such as keyword, language, country, source, and more. + Searches for articles based on specified criteria such as keywords, language, country, source, and more. Parameters ---------- - q : str - The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - - - For an exact match, use double quotes. For example, `"technology news"`. - - Use `*` to search for any keyword. - - Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. - - Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - - For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). + q : Q search_in : typing.Optional[SearchIn] @@ -175,318 +163,138 @@ def get( predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. source_name : typing.Optional[str] Word or phrase to search within the source names. To specify multiple values, use a comma-separated string. - Example: `"sport, tech"` - **Note**: The search doesn't require an exact match and returns sources containing the specified terms in their names. You can use any word or phrase, like `"sport"` or `"new york times"`. For example, `"sport"` returns sources such as `"Motorsport"`, `"Dot Esport"`, and `"Tuttosport"`. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` + from_ : typing.Optional[From] - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. + to : typing.Optional[To] - published_date_precision : typing.Optional[SearchGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. + published_date_precision : typing.Optional[PublishedDatePrecision] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + by_parse_date : typing.Optional[ByParseDate] - sort_by : typing.Optional[SearchGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. + sort_by : typing.Optional[SortBy] - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - additional_domain_info : typing.Optional[bool] - If true, includes additional domain information in the response for each article: - - `is_news_domain`: Boolean indicating if the source is a news domain. - - `news_domain_type`: Type of news domain (e.g., `"Original Content"`). - - `news_type`: Category of news (e.g., `"News and Blogs"`). + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - is_news_domain : typing.Optional[bool] - If true, filters results to include only news domains. + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - news_domain_type : typing.Optional[SearchGetRequestNewsDomainType] - Filters results based on the news domain type. Possible values are: - - `Original Content`: Sources that produce their own content. - - `Aggregator`: Sources that collect content from various other sources. - - `Press Releases`: Sources primarily publishing press releases. - - `Republisher`: Sources that republish content from other sources. - - `Other`: Sources that don't fit into main categories. - - news_type : typing.Optional[str] - Filters results based on the news type. Multiple types can be specified using a comma-separated string. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - Example: `"General News Outlets,Tech News and Updates"` - - For a complete list of available news types, see [Enumerated parameters > News type](/docs/v3/api-reference/overview/enumerated-parameters#news-type-news-type). - - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. - - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. + additional_domain_info : typing.Optional[AdditionalDomainInfo] - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + is_news_domain : typing.Optional[IsNewsDomain] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + news_domain_type : typing.Optional[NewsDomainType] - page_size : typing.Optional[int] - The number of articles to return per page. + news_type : typing.Optional[str] + Filters results based on the news type. Multiple types can be specified using a comma-separated string. - clustering_enabled : typing.Optional[bool] - Determines whether to group similar articles into clusters. If true, the API returns clustered results. + For a complete list of available news types, see [Enumerated parameters > News type](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#news-type-news-type). - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + word_count_min : typing.Optional[WordCountMin] - clustering_variable : typing.Optional[SearchGetRequestClusteringVariable] - Specifies which part of the article to use for determining similarity when clustering. + word_count_max : typing.Optional[WordCountMax] - Possible values are: - - `content`: Uses the full article content (default). - - `title`: Uses only the article title. - - `summary`: Uses the article summary. + page : typing.Optional[Page] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + page_size : typing.Optional[PageSize] - clustering_threshold : typing.Optional[float] - Sets the similarity threshold for grouping articles into clusters. A lower value creates more inclusive clusters, while a higher value requires greater similarity between articles. + clustering_enabled : typing.Optional[ClusteringEnabled] - Examples: - - `0.3`: Results in larger, more diverse clusters. - - `0.6`: Balances cluster size and article similarity (default). - - `0.9`: Creates smaller, tightly related clusters. + clustering_variable : typing.Optional[ClusteringVariable] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + clustering_threshold : typing.Optional[ClusteringThreshold] include_nlp_data : typing.Optional[IncludeNlpData] has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. + theme : typing.Optional[Theme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + not_theme : typing.Optional[NotTheme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + org_entity_name : typing.Optional[OrgEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + per_entity_name : typing.Optional[PerEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + loc_entity_name : typing.Optional[LocEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + misc_entity_name : typing.Optional[MiscEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -494,8 +302,6 @@ def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -503,8 +309,6 @@ def get( iab_tags : typing.Optional[str] Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - Example: `"Business, Events"` - **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -512,8 +316,6 @@ def get( not_iab_tags : typing.Optional[str] Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - Example: `"Agriculture, Metals"` - **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -521,19 +323,15 @@ def get( custom_tags : typing.Optional[str] Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. - - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` + - `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). + Example: `custom_tags.industry="Manufacturing,Logistics"` - exclude_duplicates : typing.Optional[bool] - If true, excludes duplicate and highly similar articles from the search results. If false, returns all relevant articles, including duplicates. + To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). - To learn more, see [Articles deduplication](/docs/v3/documentation/guides-and-concepts/articles-deduplication). + exclude_duplicates : typing.Optional[ExcludeDuplicates] - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -541,7 +339,7 @@ def get( Returns ------- HttpResponse[SearchGetResponse] - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. """ _response = self._client_wrapper.httpx_client.request( "api/search", @@ -559,8 +357,8 @@ def get( "countries": countries, "not_countries": not_countries, "not_author_name": not_author_name, - "from_": serialize_datetime(from_) if from_ is not None else None, - "to_": serialize_datetime(to) if to is not None else None, + "from_": convert_and_respect_annotation_metadata(object_=from_, annotation=From, direction="write"), + "to_": convert_and_respect_annotation_metadata(object_=to, annotation=To, direction="write"), "published_date_precision": published_date_precision, "by_parse_date": by_parse_date, "sort_by": sort_by, @@ -573,6 +371,7 @@ def get( "parent_url": parent_url, "all_links": all_links, "all_domain_links": all_domain_links, + "all_links_text": all_links_text, "additional_domain_info": additional_domain_info, "is_news_domain": is_news_domain, "news_domain_type": news_domain_type, @@ -696,6 +495,10 @@ def get( _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 post( @@ -727,6 +530,7 @@ def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, additional_domain_info: typing.Optional[AdditionalDomainInfo] = OMIT, is_news_domain: typing.Optional[IsNewsDomain] = OMIT, news_domain_type: typing.Optional[NewsDomainType] = OMIT, @@ -760,7 +564,7 @@ def post( request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[SearchPostResponse]: """ - Searches for articles based on specified criteria such as keyword, language, country, source, and more. + Searches for articles based on specified criteria such as keywords, language, country, source, and more. Parameters ---------- @@ -816,6 +620,8 @@ def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + additional_domain_info : typing.Optional[AdditionalDomainInfo] is_news_domain : typing.Optional[IsNewsDomain] @@ -882,7 +688,7 @@ def post( Returns ------- HttpResponse[SearchPostResponse] - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. """ _response = self._client_wrapper.httpx_client.request( "api/search", @@ -936,6 +742,9 @@ def post( "all_domain_links": convert_and_respect_annotation_metadata( object_=all_domain_links, annotation=AllDomainLinks, direction="write" ), + "all_links_text": convert_and_respect_annotation_metadata( + object_=all_links_text, annotation=AllLinksText, direction="write" + ), "additional_domain_info": additional_domain_info, "is_news_domain": is_news_domain, "news_domain_type": news_domain_type, @@ -951,10 +760,8 @@ def post( "clustering_threshold": clustering_threshold, "include_nlp_data": include_nlp_data, "has_nlp": has_nlp, - "theme": convert_and_respect_annotation_metadata(object_=theme, annotation=Theme, direction="write"), - "not_theme": convert_and_respect_annotation_metadata( - object_=not_theme, annotation=NotTheme, direction="write" - ), + "theme": theme, + "not_theme": not_theme, "ORG_entity_name": org_entity_name, "PER_entity_name": per_entity_name, "LOC_entity_name": loc_entity_name, @@ -1077,6 +884,10 @@ def post( _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) @@ -1087,7 +898,7 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): async def get( self, *, - q: str, + q: Q, search_in: typing.Optional[SearchIn] = None, include_translation_fields: typing.Optional[IncludeTranslationFields] = None, predefined_sources: typing.Optional[str] = None, @@ -1099,68 +910,59 @@ async def get( countries: typing.Optional[str] = None, not_countries: typing.Optional[str] = None, not_author_name: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - published_date_precision: typing.Optional[SearchGetRequestPublishedDatePrecision] = None, - by_parse_date: typing.Optional[bool] = None, - sort_by: typing.Optional[SearchGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, + from_: typing.Optional[From] = None, + to: typing.Optional[To] = None, + published_date_precision: typing.Optional[PublishedDatePrecision] = None, + by_parse_date: typing.Optional[ByParseDate] = None, + sort_by: typing.Optional[SortBy] = None, + ranked_only: typing.Optional[RankedOnly] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, + is_headline: typing.Optional[IsHeadline] = None, + is_opinion: typing.Optional[IsOpinion] = None, + is_paid_content: typing.Optional[IsPaidContent] = None, parent_url: typing.Optional[str] = None, all_links: typing.Optional[str] = None, all_domain_links: typing.Optional[str] = None, - additional_domain_info: typing.Optional[bool] = None, - is_news_domain: typing.Optional[bool] = None, - news_domain_type: typing.Optional[SearchGetRequestNewsDomainType] = None, + all_links_text: typing.Optional[str] = None, + additional_domain_info: typing.Optional[AdditionalDomainInfo] = None, + is_news_domain: typing.Optional[IsNewsDomain] = None, + news_domain_type: typing.Optional[NewsDomainType] = None, news_type: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - clustering_enabled: typing.Optional[bool] = None, - clustering_variable: typing.Optional[SearchGetRequestClusteringVariable] = None, - clustering_threshold: typing.Optional[float] = None, + word_count_min: typing.Optional[WordCountMin] = None, + word_count_max: typing.Optional[WordCountMax] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, + clustering_enabled: typing.Optional[ClusteringEnabled] = None, + clustering_variable: typing.Optional[ClusteringVariable] = None, + clustering_threshold: typing.Optional[ClusteringThreshold] = None, include_nlp_data: typing.Optional[IncludeNlpData] = None, has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - org_entity_name: typing.Optional[str] = None, - per_entity_name: typing.Optional[str] = None, - loc_entity_name: typing.Optional[str] = None, - misc_entity_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, + theme: typing.Optional[Theme] = None, + not_theme: typing.Optional[NotTheme] = None, + org_entity_name: typing.Optional[OrgEntityName] = None, + per_entity_name: typing.Optional[PerEntityName] = None, + loc_entity_name: typing.Optional[LocEntityName] = None, + misc_entity_name: typing.Optional[MiscEntityName] = None, + title_sentiment_min: typing.Optional[TitleSentimentMin] = None, + title_sentiment_max: typing.Optional[TitleSentimentMax] = None, + content_sentiment_min: typing.Optional[ContentSentimentMin] = None, + content_sentiment_max: typing.Optional[ContentSentimentMax] = None, iptc_tags: typing.Optional[str] = None, not_iptc_tags: typing.Optional[str] = None, iab_tags: typing.Optional[str] = None, not_iab_tags: typing.Optional[str] = None, custom_tags: typing.Optional[str] = None, - exclude_duplicates: typing.Optional[bool] = None, - robots_compliant: typing.Optional[bool] = None, + exclude_duplicates: typing.Optional[ExcludeDuplicates] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[SearchGetResponse]: """ - Searches for articles based on specified criteria such as keyword, language, country, source, and more. + Searches for articles based on specified criteria such as keywords, language, country, source, and more. Parameters ---------- - q : str - The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - - - For an exact match, use double quotes. For example, `"technology news"`. - - Use `*` to search for any keyword. - - Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. - - Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - - For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). + q : Q search_in : typing.Optional[SearchIn] @@ -1169,318 +971,138 @@ async def get( predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. source_name : typing.Optional[str] Word or phrase to search within the source names. To specify multiple values, use a comma-separated string. - Example: `"sport, tech"` - **Note**: The search doesn't require an exact match and returns sources containing the specified terms in their names. You can use any word or phrase, like `"sport"` or `"new york times"`. For example, `"sport"` returns sources such as `"Motorsport"`, `"Dot Esport"`, and `"Tuttosport"`. sources : typing.Optional[str] One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - not_sources : typing.Optional[str] The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - Example: `"cnn.com, wsj.com"` - lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). not_lang : typing.Optional[str] The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_countries : typing.Optional[str] The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). not_author_name : typing.Optional[str] The list of author names to exclude from your search. To exclude articles by specific authors, use a comma-separated string. - Example: `"John Doe, Jane Doe"` - - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` + from_ : typing.Optional[From] - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. + to : typing.Optional[To] - published_date_precision : typing.Optional[SearchGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. + published_date_precision : typing.Optional[PublishedDatePrecision] - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. + by_parse_date : typing.Optional[ByParseDate] - sort_by : typing.Optional[SearchGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. + sort_by : typing.Optional[SortBy] - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. + ranked_only : typing.Optional[RankedOnly] - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. + is_headline : typing.Optional[IsHeadline] - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. + is_opinion : typing.Optional[IsOpinion] - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. + is_paid_content : typing.Optional[IsPaidContent] parent_url : typing.Optional[str] The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - Example: `"wsj.com/politics, wsj.com/tech"` - all_links : typing.Optional[str] The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). all_domain_links : typing.Optional[str] The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - Example: `"who.int, nih.gov"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). + For more details, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - additional_domain_info : typing.Optional[bool] - If true, includes additional domain information in the response for each article: - - `is_news_domain`: Boolean indicating if the source is a news domain. - - `news_domain_type`: Type of news domain (e.g., `"Original Content"`). - - `news_type`: Category of news (e.g., `"News and Blogs"`). + all_links_text : typing.Optional[str] + The text content of links mentioned in the article. Searches for links where the anchor text contains the specified terms. For multiple terms, use a comma-separated string. - is_news_domain : typing.Optional[bool] - If true, filters results to include only news domains. + **Note**: When this parameter is used, the response includes the `all_links_data` field with detailed link information. - news_domain_type : typing.Optional[SearchGetRequestNewsDomainType] - Filters results based on the news domain type. Possible values are: - - `Original Content`: Sources that produce their own content. - - `Aggregator`: Sources that collect content from various other sources. - - `Press Releases`: Sources primarily publishing press releases. - - `Republisher`: Sources that republish content from other sources. - - `Other`: Sources that don't fit into main categories. - - news_type : typing.Optional[str] - Filters results based on the news type. Multiple types can be specified using a comma-separated string. + To learn more, see [Search by URL](https://www.newscatcherapi.com/docs/news-api/how-to/search-by-url). - Example: `"General News Outlets,Tech News and Updates"` - - For a complete list of available news types, see [Enumerated parameters > News type](/docs/v3/api-reference/overview/enumerated-parameters#news-type-news-type). - - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. - - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. + additional_domain_info : typing.Optional[AdditionalDomainInfo] - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. + is_news_domain : typing.Optional[IsNewsDomain] - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + news_domain_type : typing.Optional[NewsDomainType] - page_size : typing.Optional[int] - The number of articles to return per page. + news_type : typing.Optional[str] + Filters results based on the news type. Multiple types can be specified using a comma-separated string. - clustering_enabled : typing.Optional[bool] - Determines whether to group similar articles into clusters. If true, the API returns clustered results. + For a complete list of available news types, see [Enumerated parameters > News type](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#news-type-news-type). - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + word_count_min : typing.Optional[WordCountMin] - clustering_variable : typing.Optional[SearchGetRequestClusteringVariable] - Specifies which part of the article to use for determining similarity when clustering. + word_count_max : typing.Optional[WordCountMax] - Possible values are: - - `content`: Uses the full article content (default). - - `title`: Uses only the article title. - - `summary`: Uses the article summary. + page : typing.Optional[Page] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + page_size : typing.Optional[PageSize] - clustering_threshold : typing.Optional[float] - Sets the similarity threshold for grouping articles into clusters. A lower value creates more inclusive clusters, while a higher value requires greater similarity between articles. + clustering_enabled : typing.Optional[ClusteringEnabled] - Examples: - - `0.3`: Results in larger, more diverse clusters. - - `0.6`: Balances cluster size and article similarity (default). - - `0.9`: Creates smaller, tightly related clusters. + clustering_variable : typing.Optional[ClusteringVariable] - To learn more, see [Clustering news articles](/docs/v3/documentation/guides-and-concepts/clustering-news-articles). + clustering_threshold : typing.Optional[ClusteringThreshold] include_nlp_data : typing.Optional[IncludeNlpData] has_nlp : typing.Optional[HasNlp] - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - org_entity_name : typing.Optional[str] - Filters articles that mention specific organization names, as identified by NLP analysis. To specify multiple organizations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Apple, Microsoft"` - - **Note**: The `ORG_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - per_entity_name : typing.Optional[str] - Filters articles that mention specific person names, as identified by NLP analysis. To specify multiple names, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Elon Musk, Jeff Bezos"` - - **Note**: The `PER_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - loc_entity_name : typing.Optional[str] - Filters articles that mention specific location names, as identified by NLP analysis. To specify multiple locations, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"California, New York"` - - **Note**: The `LOC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - misc_entity_name : typing.Optional[str] - Filters articles that mention other named entities not falling under person, organization, or location categories. Includes events, nationalities, products, works of art, and more. To specify multiple entities, use a comma-separated string. To search named entities in translations, combine with the translation options of the `search_in` parameter (e.g., `title_content_translated`). - - Example: `"Bitcoin, Blockchain"` - - **Note**: The `MISC_entity_name` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [Search by entity](/docs/v3/documentation/how-to/search-by-entity). - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. + theme : typing.Optional[Theme] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + not_theme : typing.Optional[NotTheme] - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. + org_entity_name : typing.Optional[OrgEntityName] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + per_entity_name : typing.Optional[PerEntityName] - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. + loc_entity_name : typing.Optional[LocEntityName] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + misc_entity_name : typing.Optional[MiscEntityName] - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. + title_sentiment_min : typing.Optional[TitleSentimentMin] - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. + title_sentiment_max : typing.Optional[TitleSentimentMax] - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. + content_sentiment_min : typing.Optional[ContentSentimentMin] - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). + content_sentiment_max : typing.Optional[ContentSentimentMax] iptc_tags : typing.Optional[str] Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - Example: `"20000199, 20000209"` - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1488,8 +1110,6 @@ async def get( not_iptc_tags : typing.Optional[str] Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - Example: `"20000205, 20000209"` - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). @@ -1497,8 +1117,6 @@ async def get( iab_tags : typing.Optional[str] Filters articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories, use a comma-separated string. - Example: `"Business, Events"` - **Note**: The `iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -1506,8 +1124,6 @@ async def get( not_iab_tags : typing.Optional[str] Inverse of the `iab_tags` parameter. Excludes articles based on Interactive Advertising Bureau (IAB) content categories. These tags provide a standardized taxonomy for digital advertising content categorization. To specify multiple IAB categories to exclude, use a comma-separated string. - Example: `"Agriculture, Metals"` - **Note**: The `not_iab_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. To learn more, see the [IAB Content taxonomy](https://iabtechlab.com/standards/content-taxonomy/). @@ -1515,19 +1131,15 @@ async def get( custom_tags : typing.Optional[str] Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. - - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` + - `custom_tags.taxonomy=Tag1,Tag2`, where `taxonomy` is the taxonomy name and `Tag1,Tag2` is a comma-separated list of tag names. - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). + Example: `custom_tags.industry="Manufacturing,Logistics"` - exclude_duplicates : typing.Optional[bool] - If true, excludes duplicate and highly similar articles from the search results. If false, returns all relevant articles, including duplicates. + To learn more, see the [Custom tags](https://www.newscatcherapi.com/docs/news-api/guides-and-concepts/custom-tags). - To learn more, see [Articles deduplication](/docs/v3/documentation/guides-and-concepts/articles-deduplication). + exclude_duplicates : typing.Optional[ExcludeDuplicates] - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1535,7 +1147,7 @@ async def get( Returns ------- AsyncHttpResponse[SearchGetResponse] - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. """ _response = await self._client_wrapper.httpx_client.request( "api/search", @@ -1553,8 +1165,8 @@ async def get( "countries": countries, "not_countries": not_countries, "not_author_name": not_author_name, - "from_": serialize_datetime(from_) if from_ is not None else None, - "to_": serialize_datetime(to) if to is not None else None, + "from_": convert_and_respect_annotation_metadata(object_=from_, annotation=From, direction="write"), + "to_": convert_and_respect_annotation_metadata(object_=to, annotation=To, direction="write"), "published_date_precision": published_date_precision, "by_parse_date": by_parse_date, "sort_by": sort_by, @@ -1567,6 +1179,7 @@ async def get( "parent_url": parent_url, "all_links": all_links, "all_domain_links": all_domain_links, + "all_links_text": all_links_text, "additional_domain_info": additional_domain_info, "is_news_domain": is_news_domain, "news_domain_type": news_domain_type, @@ -1690,6 +1303,10 @@ async def get( _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 post( @@ -1721,6 +1338,7 @@ async def post( parent_url: typing.Optional[ParentUrl] = OMIT, all_links: typing.Optional[AllLinks] = OMIT, all_domain_links: typing.Optional[AllDomainLinks] = OMIT, + all_links_text: typing.Optional[AllLinksText] = OMIT, additional_domain_info: typing.Optional[AdditionalDomainInfo] = OMIT, is_news_domain: typing.Optional[IsNewsDomain] = OMIT, news_domain_type: typing.Optional[NewsDomainType] = OMIT, @@ -1754,7 +1372,7 @@ async def post( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[SearchPostResponse]: """ - Searches for articles based on specified criteria such as keyword, language, country, source, and more. + Searches for articles based on specified criteria such as keywords, language, country, source, and more. Parameters ---------- @@ -1810,6 +1428,8 @@ async def post( all_domain_links : typing.Optional[AllDomainLinks] + all_links_text : typing.Optional[AllLinksText] + additional_domain_info : typing.Optional[AdditionalDomainInfo] is_news_domain : typing.Optional[IsNewsDomain] @@ -1876,7 +1496,7 @@ async def post( Returns ------- AsyncHttpResponse[SearchPostResponse] - A successful response containing articles that match the specified search criteria. The response may include clustering information if enabled. + A successful response containing articles that match the search criteria. When `clustering_enabled` is `true`, returns a Clustered Articles Response. Otherwise, returns a Search Response. """ _response = await self._client_wrapper.httpx_client.request( "api/search", @@ -1930,6 +1550,9 @@ async def post( "all_domain_links": convert_and_respect_annotation_metadata( object_=all_domain_links, annotation=AllDomainLinks, direction="write" ), + "all_links_text": convert_and_respect_annotation_metadata( + object_=all_links_text, annotation=AllLinksText, direction="write" + ), "additional_domain_info": additional_domain_info, "is_news_domain": is_news_domain, "news_domain_type": news_domain_type, @@ -1945,10 +1568,8 @@ async def post( "clustering_threshold": clustering_threshold, "include_nlp_data": include_nlp_data, "has_nlp": has_nlp, - "theme": convert_and_respect_annotation_metadata(object_=theme, annotation=Theme, direction="write"), - "not_theme": convert_and_respect_annotation_metadata( - object_=not_theme, annotation=NotTheme, direction="write" - ), + "theme": theme, + "not_theme": not_theme, "ORG_entity_name": org_entity_name, "PER_entity_name": per_entity_name, "LOC_entity_name": loc_entity_name, @@ -2071,4 +1692,8 @@ async def post( _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) diff --git a/src/newscatcher/search/types/__init__.py b/src/newscatcher/search/types/__init__.py index e6be70d..5dbeefd 100644 --- a/src/newscatcher/search/types/__init__.py +++ b/src/newscatcher/search/types/__init__.py @@ -2,18 +2,37 @@ # isort: skip_file -from .search_get_request_clustering_variable import SearchGetRequestClusteringVariable -from .search_get_request_news_domain_type import SearchGetRequestNewsDomainType -from .search_get_request_published_date_precision import SearchGetRequestPublishedDatePrecision -from .search_get_request_sort_by import SearchGetRequestSortBy -from .search_get_response import SearchGetResponse -from .search_post_response import SearchPostResponse - -__all__ = [ - "SearchGetRequestClusteringVariable", - "SearchGetRequestNewsDomainType", - "SearchGetRequestPublishedDatePrecision", - "SearchGetRequestSortBy", - "SearchGetResponse", - "SearchPostResponse", -] +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .search_get_response import SearchGetResponse + from .search_post_response import SearchPostResponse +_dynamic_imports: typing.Dict[str, str] = { + "SearchGetResponse": ".search_get_response", + "SearchPostResponse": ".search_post_response", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["SearchGetResponse", "SearchPostResponse"] diff --git a/src/newscatcher/search/types/search_get_request_clustering_variable.py b/src/newscatcher/search/types/search_get_request_clustering_variable.py deleted file mode 100644 index e721276..0000000 --- a/src/newscatcher/search/types/search_get_request_clustering_variable.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -SearchGetRequestClusteringVariable = typing.Union[typing.Literal["content", "title", "summary"], typing.Any] diff --git a/src/newscatcher/search/types/search_get_request_news_domain_type.py b/src/newscatcher/search/types/search_get_request_news_domain_type.py deleted file mode 100644 index 6548490..0000000 --- a/src/newscatcher/search/types/search_get_request_news_domain_type.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -SearchGetRequestNewsDomainType = typing.Union[ - typing.Literal["Original Content", "Aggregator", "Press Releases", "Republisher", "Other"], typing.Any -] diff --git a/src/newscatcher/search/types/search_get_request_published_date_precision.py b/src/newscatcher/search/types/search_get_request_published_date_precision.py deleted file mode 100644 index 5f991a8..0000000 --- a/src/newscatcher/search/types/search_get_request_published_date_precision.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -SearchGetRequestPublishedDatePrecision = typing.Union[typing.Literal["full", "timezone unknown", "date"], typing.Any] diff --git a/src/newscatcher/search/types/search_get_request_sort_by.py b/src/newscatcher/search/types/search_get_request_sort_by.py deleted file mode 100644 index b93fcb6..0000000 --- a/src/newscatcher/search/types/search_get_request_sort_by.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -SearchGetRequestSortBy = typing.Union[typing.Literal["relevancy", "date", "rank"], typing.Any] diff --git a/src/newscatcher/search_link/__init__.py b/src/newscatcher/search_by_link/__init__.py similarity index 100% rename from src/newscatcher/search_link/__init__.py rename to src/newscatcher/search_by_link/__init__.py diff --git a/src/newscatcher/search_link/client.py b/src/newscatcher/search_by_link/client.py similarity index 73% rename from src/newscatcher/search_link/client.py rename to src/newscatcher/search_by_link/client.py index e0ad325..2c2e184 100644 --- a/src/newscatcher/search_link/client.py +++ b/src/newscatcher/search_by_link/client.py @@ -12,37 +12,37 @@ from ..types.robots_compliant import RobotsCompliant from ..types.search_response_dto import SearchResponseDto from ..types.to import To -from .raw_client import AsyncRawSearchLinkClient, RawSearchLinkClient +from .raw_client import AsyncRawSearchByLinkClient, RawSearchByLinkClient # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) -class SearchLinkClient: +class SearchByLinkClient: def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawSearchLinkClient(client_wrapper=client_wrapper) + self._raw_client = RawSearchByLinkClient(client_wrapper=client_wrapper) @property - def with_raw_response(self) -> RawSearchLinkClient: + def with_raw_response(self) -> RawSearchByLinkClient: """ Retrieves a raw implementation of this client that returns raw responses. Returns ------- - RawSearchLinkClient + RawSearchByLinkClient """ return self._raw_client - def search_url_get( + def search_by_link_get( self, *, ids: typing.Optional[str] = None, links: typing.Optional[str] = None, from_: typing.Optional[From] = None, to: typing.Optional[To] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - robots_compliant: typing.Optional[bool] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> SearchResponseDto: """ @@ -53,31 +53,22 @@ def search_url_get( ids : typing.Optional[str] The Newscatcher article ID (corresponds to the `_id` field in API response) or a list of article IDs to search for. To specify multiple IDs, use a comma-separated string. - Example: `"1234567890abcdef, abcdef1234567890"` - **Caution**: You can use either the `links` or the `ids` parameter, but not both at the same time. links : typing.Optional[str] The article link or list of article links to search for. To specify multiple links, use a comma-separated string. - Example: `"https://example.com/article1, https://example.com/article2"` - **Caution**: You can use either the `links` or the `ids` parameter, but not both at the same time. from_ : typing.Optional[From] to : typing.Optional[To] - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + page : typing.Optional[Page] - page_size : typing.Optional[int] - The number of articles to return per page. + page_size : typing.Optional[PageSize] - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -96,16 +87,21 @@ def search_url_get( client = NewscatcherApi( api_key="YOUR_API_KEY", ) - client.search_link.search_url_get( + client.search_by_link.search_by_link_get( + ids="5f8d0d55b6e45e00179c6e7e", + links="https://nytimes.com/article1,https://bbc.com/article2", from_=datetime.datetime.fromisoformat( "2024-07-01 00:00:00+00:00", ), to=datetime.datetime.fromisoformat( "2024-01-01 00:00:00+00:00", ), + page=2, + page_size=50, + robots_compliant=True, ) """ - _response = self._raw_client.search_url_get( + _response = self._raw_client.search_by_link_get( ids=ids, links=links, from_=from_, @@ -117,7 +113,7 @@ def search_url_get( ) return _response.data - def search_url_post( + def search_by_link_post( self, *, ids: typing.Optional[Ids] = OMIT, @@ -179,18 +175,11 @@ def search_url_post( client = NewscatcherApi( api_key="YOUR_API_KEY", ) - client.search_link.search_url_post( - ids=[ - "8ea8a784568ffaa05cb6d1ab2d2e84dd", - "0146a551ef05ab1c494a55e806e3ce64", - ], - links=[ - "https://www.nytimes.com/2024/08/30/technology/ai-chatbot-chatgpt-manipulation.html", - "https://www.bbc.com/news/articles/c39k379grzlo", - ], + client.search_by_link.search_by_link_post( + links="https://www.reuters.com/business/energy/oil-prices-up-after-israeli-attacks-oversupply-caps-gains-2025-09-10/", ) """ - _response = self._raw_client.search_url_post( + _response = self._raw_client.search_by_link_post( ids=ids, links=links, from_=from_, @@ -203,31 +192,31 @@ def search_url_post( return _response.data -class AsyncSearchLinkClient: +class AsyncSearchByLinkClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawSearchLinkClient(client_wrapper=client_wrapper) + self._raw_client = AsyncRawSearchByLinkClient(client_wrapper=client_wrapper) @property - def with_raw_response(self) -> AsyncRawSearchLinkClient: + def with_raw_response(self) -> AsyncRawSearchByLinkClient: """ Retrieves a raw implementation of this client that returns raw responses. Returns ------- - AsyncRawSearchLinkClient + AsyncRawSearchByLinkClient """ return self._raw_client - async def search_url_get( + async def search_by_link_get( self, *, ids: typing.Optional[str] = None, links: typing.Optional[str] = None, from_: typing.Optional[From] = None, to: typing.Optional[To] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - robots_compliant: typing.Optional[bool] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> SearchResponseDto: """ @@ -238,31 +227,22 @@ async def search_url_get( ids : typing.Optional[str] The Newscatcher article ID (corresponds to the `_id` field in API response) or a list of article IDs to search for. To specify multiple IDs, use a comma-separated string. - Example: `"1234567890abcdef, abcdef1234567890"` - **Caution**: You can use either the `links` or the `ids` parameter, but not both at the same time. links : typing.Optional[str] The article link or list of article links to search for. To specify multiple links, use a comma-separated string. - Example: `"https://example.com/article1, https://example.com/article2"` - **Caution**: You can use either the `links` or the `ids` parameter, but not both at the same time. from_ : typing.Optional[From] to : typing.Optional[To] - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + page : typing.Optional[Page] - page_size : typing.Optional[int] - The number of articles to return per page. + page_size : typing.Optional[PageSize] - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -285,19 +265,24 @@ async def search_url_get( async def main() -> None: - await client.search_link.search_url_get( + await client.search_by_link.search_by_link_get( + ids="5f8d0d55b6e45e00179c6e7e", + links="https://nytimes.com/article1,https://bbc.com/article2", from_=datetime.datetime.fromisoformat( "2024-07-01 00:00:00+00:00", ), to=datetime.datetime.fromisoformat( "2024-01-01 00:00:00+00:00", ), + page=2, + page_size=50, + robots_compliant=True, ) asyncio.run(main()) """ - _response = await self._raw_client.search_url_get( + _response = await self._raw_client.search_by_link_get( ids=ids, links=links, from_=from_, @@ -309,7 +294,7 @@ async def main() -> None: ) return _response.data - async def search_url_post( + async def search_by_link_post( self, *, ids: typing.Optional[Ids] = OMIT, @@ -376,21 +361,14 @@ async def search_url_post( async def main() -> None: - await client.search_link.search_url_post( - ids=[ - "8ea8a784568ffaa05cb6d1ab2d2e84dd", - "0146a551ef05ab1c494a55e806e3ce64", - ], - links=[ - "https://www.nytimes.com/2024/08/30/technology/ai-chatbot-chatgpt-manipulation.html", - "https://www.bbc.com/news/articles/c39k379grzlo", - ], + await client.search_by_link.search_by_link_post( + links="https://www.reuters.com/business/energy/oil-prices-up-after-israeli-attacks-oversupply-caps-gains-2025-09-10/", ) asyncio.run(main()) """ - _response = await self._raw_client.search_url_post( + _response = await self._raw_client.search_by_link_post( ids=ids, links=links, from_=from_, diff --git a/src/newscatcher/search_link/raw_client.py b/src/newscatcher/search_by_link/raw_client.py similarity index 92% rename from src/newscatcher/search_link/raw_client.py rename to src/newscatcher/search_by_link/raw_client.py index 9823286..f0751d8 100644 --- a/src/newscatcher/search_link/raw_client.py +++ b/src/newscatcher/search_by_link/raw_client.py @@ -6,6 +6,7 @@ from ..core.api_error import ApiError from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.parse_error import ParsingError from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..core.serialization import convert_and_respect_annotation_metadata @@ -25,25 +26,26 @@ from ..types.robots_compliant import RobotsCompliant from ..types.search_response_dto import SearchResponseDto from ..types.to import To +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) -class RawSearchLinkClient: +class RawSearchByLinkClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def search_url_get( + def search_by_link_get( self, *, ids: typing.Optional[str] = None, links: typing.Optional[str] = None, from_: typing.Optional[From] = None, to: typing.Optional[To] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - robots_compliant: typing.Optional[bool] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[SearchResponseDto]: """ @@ -54,31 +56,22 @@ def search_url_get( ids : typing.Optional[str] The Newscatcher article ID (corresponds to the `_id` field in API response) or a list of article IDs to search for. To specify multiple IDs, use a comma-separated string. - Example: `"1234567890abcdef, abcdef1234567890"` - **Caution**: You can use either the `links` or the `ids` parameter, but not both at the same time. links : typing.Optional[str] The article link or list of article links to search for. To specify multiple links, use a comma-separated string. - Example: `"https://example.com/article1, https://example.com/article2"` - **Caution**: You can use either the `links` or the `ids` parameter, but not both at the same time. from_ : typing.Optional[From] to : typing.Optional[To] - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + page : typing.Optional[Page] - page_size : typing.Optional[int] - The number of articles to return per page. + page_size : typing.Optional[PageSize] - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -192,9 +185,13 @@ def search_url_get( _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 search_url_post( + def search_by_link_post( self, *, ids: typing.Optional[Ids] = OMIT, @@ -357,23 +354,27 @@ def search_url_post( _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) -class AsyncRawSearchLinkClient: +class AsyncRawSearchByLinkClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def search_url_get( + async def search_by_link_get( self, *, ids: typing.Optional[str] = None, links: typing.Optional[str] = None, from_: typing.Optional[From] = None, to: typing.Optional[To] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - robots_compliant: typing.Optional[bool] = None, + page: typing.Optional[Page] = None, + page_size: typing.Optional[PageSize] = None, + robots_compliant: typing.Optional[RobotsCompliant] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[SearchResponseDto]: """ @@ -384,31 +385,22 @@ async def search_url_get( ids : typing.Optional[str] The Newscatcher article ID (corresponds to the `_id` field in API response) or a list of article IDs to search for. To specify multiple IDs, use a comma-separated string. - Example: `"1234567890abcdef, abcdef1234567890"` - **Caution**: You can use either the `links` or the `ids` parameter, but not both at the same time. links : typing.Optional[str] The article link or list of article links to search for. To specify multiple links, use a comma-separated string. - Example: `"https://example.com/article1, https://example.com/article2"` - **Caution**: You can use either the `links` or the `ids` parameter, but not both at the same time. from_ : typing.Optional[From] to : typing.Optional[To] - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). + page : typing.Optional[Page] - page_size : typing.Optional[int] - The number of articles to return per page. + page_size : typing.Optional[PageSize] - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. + robots_compliant : typing.Optional[RobotsCompliant] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -522,9 +514,13 @@ async def search_url_get( _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 search_url_post( + async def search_by_link_post( self, *, ids: typing.Optional[Ids] = OMIT, @@ -687,4 +683,8 @@ async def search_url_post( _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) diff --git a/src/newscatcher/searchsimilar/__init__.py b/src/newscatcher/searchsimilar/__init__.py deleted file mode 100644 index da93e88..0000000 --- a/src/newscatcher/searchsimilar/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -from .types import ( - SearchSimilarGetRequestPublishedDatePrecision, - SearchSimilarGetRequestSortBy, - SearchSimilarGetResponse, - SearchSimilarPostResponse, -) - -__all__ = [ - "SearchSimilarGetRequestPublishedDatePrecision", - "SearchSimilarGetRequestSortBy", - "SearchSimilarGetResponse", - "SearchSimilarPostResponse", -] diff --git a/src/newscatcher/searchsimilar/client.py b/src/newscatcher/searchsimilar/client.py deleted file mode 100644 index e5659d4..0000000 --- a/src/newscatcher/searchsimilar/client.py +++ /dev/null @@ -1,1360 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing - -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.request_options import RequestOptions -from ..types.all_domain_links import AllDomainLinks -from ..types.all_links import AllLinks -from ..types.by_parse_date import ByParseDate -from ..types.content_sentiment_max import ContentSentimentMax -from ..types.content_sentiment_min import ContentSentimentMin -from ..types.countries import Countries -from ..types.custom_tags import CustomTags -from ..types.from_ import From -from ..types.from_rank import FromRank -from ..types.has_nlp import HasNlp -from ..types.include_nlp_data import IncludeNlpData -from ..types.include_similar_documents import IncludeSimilarDocuments -from ..types.include_translation_fields import IncludeTranslationFields -from ..types.iptc_tags import IptcTags -from ..types.is_headline import IsHeadline -from ..types.is_opinion import IsOpinion -from ..types.is_paid_content import IsPaidContent -from ..types.lang import Lang -from ..types.ner_name import NerName -from ..types.not_countries import NotCountries -from ..types.not_iptc_tags import NotIptcTags -from ..types.not_lang import NotLang -from ..types.not_sources import NotSources -from ..types.not_theme import NotTheme -from ..types.page import Page -from ..types.page_size import PageSize -from ..types.parent_url import ParentUrl -from ..types.predefined_sources import PredefinedSources -from ..types.published_date_precision import PublishedDatePrecision -from ..types.q import Q -from ..types.ranked_only import RankedOnly -from ..types.robots_compliant import RobotsCompliant -from ..types.search_in import SearchIn -from ..types.similar_documents_fields import SimilarDocumentsFields -from ..types.similar_documents_number import SimilarDocumentsNumber -from ..types.sort_by import SortBy -from ..types.sources import Sources -from ..types.theme import Theme -from ..types.title_sentiment_max import TitleSentimentMax -from ..types.title_sentiment_min import TitleSentimentMin -from ..types.to import To -from ..types.to_rank import ToRank -from ..types.word_count_max import WordCountMax -from ..types.word_count_min import WordCountMin -from .raw_client import AsyncRawSearchsimilarClient, RawSearchsimilarClient -from .types.search_similar_get_request_published_date_precision import SearchSimilarGetRequestPublishedDatePrecision -from .types.search_similar_get_request_sort_by import SearchSimilarGetRequestSortBy -from .types.search_similar_get_response import SearchSimilarGetResponse -from .types.search_similar_post_response import SearchSimilarPostResponse - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class SearchsimilarClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawSearchsimilarClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawSearchsimilarClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawSearchsimilarClient - """ - return self._raw_client - - def get( - self, - *, - q: str, - search_in: typing.Optional[SearchIn] = None, - include_translation_fields: typing.Optional[IncludeTranslationFields] = None, - include_similar_documents: typing.Optional[bool] = None, - similar_documents_number: typing.Optional[int] = None, - similar_documents_fields: typing.Optional[str] = None, - predefined_sources: typing.Optional[str] = None, - sources: typing.Optional[str] = None, - not_sources: typing.Optional[str] = None, - lang: typing.Optional[str] = None, - not_lang: typing.Optional[str] = None, - countries: typing.Optional[str] = None, - not_countries: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - by_parse_date: typing.Optional[bool] = None, - published_date_precision: typing.Optional[SearchSimilarGetRequestPublishedDatePrecision] = None, - sort_by: typing.Optional[SearchSimilarGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, - parent_url: typing.Optional[str] = None, - all_links: typing.Optional[str] = None, - all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - include_nlp_data: typing.Optional[IncludeNlpData] = None, - has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - ner_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, - iptc_tags: typing.Optional[str] = None, - not_iptc_tags: typing.Optional[str] = None, - custom_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SearchSimilarGetResponse: - """ - Searches for articles similar to a specified query. - - Parameters - ---------- - q : str - The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - - - For an exact match, use double quotes. For example, `"technology news"`. - - Use `*` to search for any keyword. - - Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. - - Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - - For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). - - search_in : typing.Optional[SearchIn] - - include_translation_fields : typing.Optional[IncludeTranslationFields] - - include_similar_documents : typing.Optional[bool] - If true, includes similar documents in the response. - - similar_documents_number : typing.Optional[int] - The number of similar documents to return. - - similar_documents_fields : typing.Optional[str] - The fields to consider for finding similar documents. - - predefined_sources : typing.Optional[str] - Predefined top news sources per country. - - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. - - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` - - sources : typing.Optional[str] - One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - - not_sources : typing.Optional[str] - The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - - Example: `"cnn.com, wsj.com"` - - lang : typing.Optional[str] - The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). - - not_lang : typing.Optional[str] - The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). - - countries : typing.Optional[str] - The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). - - not_countries : typing.Optional[str] - The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). - - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. - - published_date_precision : typing.Optional[SearchSimilarGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. - - sort_by : typing.Optional[SearchSimilarGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. - - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. - - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. - - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. - - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. - - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. - - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. - - parent_url : typing.Optional[str] - The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - - Example: `"wsj.com/politics, wsj.com/tech"` - - all_links : typing.Optional[str] - The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). - - all_domain_links : typing.Optional[str] - The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - - Example: `"who.int, nih.gov"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). - - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. - - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. - - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). - - page_size : typing.Optional[int] - The number of articles to return per page. - - include_nlp_data : typing.Optional[IncludeNlpData] - - has_nlp : typing.Optional[HasNlp] - - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - ner_name : typing.Optional[str] - The name of person, organization, location, product or other named entity to search for. To specify multiple names use a comma-separated string. - - Example: `"Tesla, Amazon"` - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - iptc_tags : typing.Optional[str] - Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - - Example: `"20000199, 20000209"` - - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. - - To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). - - not_iptc_tags : typing.Optional[str] - Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - - Example: `"20000205, 20000209"` - - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. - - To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). - - custom_tags : typing.Optional[str] - Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. - - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` - - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). - - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SearchSimilarGetResponse - A successful response containing articles similar to the specified query. If no matches, returns a failed search response according to the defined schema. - - Examples - -------- - import datetime - - from newscatcher import NewscatcherApi - - client = NewscatcherApi( - api_key="YOUR_API_KEY", - ) - client.searchsimilar.get( - q="technology AND (Apple OR Microsoft) NOT Google", - search_in="title_content, title_content_translated", - include_translation_fields=True, - similar_documents_fields="title,summary", - predefined_sources="top 100 US, top 5 GB", - from_=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", - ), - include_nlp_data=True, - has_nlp=True, - theme="Business,Finance", - not_theme="Crime", - ner_name="Tesla", - iptc_tags="20000199,20000209", - not_iptc_tags="20000205,20000209", - custom_tags="Tag1,Tag2,Tag3", - ) - """ - _response = self._raw_client.get( - q=q, - search_in=search_in, - include_translation_fields=include_translation_fields, - include_similar_documents=include_similar_documents, - similar_documents_number=similar_documents_number, - similar_documents_fields=similar_documents_fields, - predefined_sources=predefined_sources, - sources=sources, - not_sources=not_sources, - lang=lang, - not_lang=not_lang, - countries=countries, - not_countries=not_countries, - from_=from_, - to=to, - by_parse_date=by_parse_date, - published_date_precision=published_date_precision, - sort_by=sort_by, - ranked_only=ranked_only, - from_rank=from_rank, - to_rank=to_rank, - is_headline=is_headline, - is_opinion=is_opinion, - is_paid_content=is_paid_content, - parent_url=parent_url, - all_links=all_links, - all_domain_links=all_domain_links, - word_count_min=word_count_min, - word_count_max=word_count_max, - page=page, - page_size=page_size, - include_nlp_data=include_nlp_data, - has_nlp=has_nlp, - theme=theme, - not_theme=not_theme, - ner_name=ner_name, - title_sentiment_min=title_sentiment_min, - title_sentiment_max=title_sentiment_max, - content_sentiment_min=content_sentiment_min, - content_sentiment_max=content_sentiment_max, - iptc_tags=iptc_tags, - not_iptc_tags=not_iptc_tags, - custom_tags=custom_tags, - robots_compliant=robots_compliant, - request_options=request_options, - ) - return _response.data - - def post( - self, - *, - q: Q, - search_in: typing.Optional[SearchIn] = OMIT, - include_translation_fields: typing.Optional[IncludeTranslationFields] = OMIT, - include_similar_documents: typing.Optional[IncludeSimilarDocuments] = OMIT, - similar_documents_number: typing.Optional[SimilarDocumentsNumber] = OMIT, - similar_documents_fields: typing.Optional[SimilarDocumentsFields] = OMIT, - predefined_sources: typing.Optional[PredefinedSources] = OMIT, - sources: typing.Optional[Sources] = OMIT, - not_sources: typing.Optional[NotSources] = OMIT, - lang: typing.Optional[Lang] = OMIT, - not_lang: typing.Optional[NotLang] = OMIT, - countries: typing.Optional[Countries] = OMIT, - not_countries: typing.Optional[NotCountries] = OMIT, - from_: typing.Optional[From] = OMIT, - to: typing.Optional[To] = OMIT, - by_parse_date: typing.Optional[ByParseDate] = OMIT, - published_date_precision: typing.Optional[PublishedDatePrecision] = OMIT, - sort_by: typing.Optional[SortBy] = OMIT, - ranked_only: typing.Optional[RankedOnly] = OMIT, - from_rank: typing.Optional[FromRank] = OMIT, - to_rank: typing.Optional[ToRank] = OMIT, - is_headline: typing.Optional[IsHeadline] = OMIT, - is_opinion: typing.Optional[IsOpinion] = OMIT, - is_paid_content: typing.Optional[IsPaidContent] = OMIT, - parent_url: typing.Optional[ParentUrl] = OMIT, - all_links: typing.Optional[AllLinks] = OMIT, - all_domain_links: typing.Optional[AllDomainLinks] = OMIT, - word_count_min: typing.Optional[WordCountMin] = OMIT, - word_count_max: typing.Optional[WordCountMax] = OMIT, - page: typing.Optional[Page] = OMIT, - page_size: typing.Optional[PageSize] = OMIT, - include_nlp_data: typing.Optional[IncludeNlpData] = OMIT, - has_nlp: typing.Optional[HasNlp] = OMIT, - theme: typing.Optional[Theme] = OMIT, - not_theme: typing.Optional[NotTheme] = OMIT, - ner_name: typing.Optional[NerName] = OMIT, - title_sentiment_min: typing.Optional[TitleSentimentMin] = OMIT, - title_sentiment_max: typing.Optional[TitleSentimentMax] = OMIT, - content_sentiment_min: typing.Optional[ContentSentimentMin] = OMIT, - content_sentiment_max: typing.Optional[ContentSentimentMax] = OMIT, - iptc_tags: typing.Optional[IptcTags] = OMIT, - not_iptc_tags: typing.Optional[NotIptcTags] = OMIT, - custom_tags: typing.Optional[CustomTags] = OMIT, - robots_compliant: typing.Optional[RobotsCompliant] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> SearchSimilarPostResponse: - """ - Searches for articles similar to the specified query. You can filter results by language, country, source, and more. - - Parameters - ---------- - q : Q - - search_in : typing.Optional[SearchIn] - - include_translation_fields : typing.Optional[IncludeTranslationFields] - - include_similar_documents : typing.Optional[IncludeSimilarDocuments] - - similar_documents_number : typing.Optional[SimilarDocumentsNumber] - - similar_documents_fields : typing.Optional[SimilarDocumentsFields] - - predefined_sources : typing.Optional[PredefinedSources] - - sources : typing.Optional[Sources] - - not_sources : typing.Optional[NotSources] - - lang : typing.Optional[Lang] - - not_lang : typing.Optional[NotLang] - - countries : typing.Optional[Countries] - - not_countries : typing.Optional[NotCountries] - - from_ : typing.Optional[From] - - to : typing.Optional[To] - - by_parse_date : typing.Optional[ByParseDate] - - published_date_precision : typing.Optional[PublishedDatePrecision] - - sort_by : typing.Optional[SortBy] - - ranked_only : typing.Optional[RankedOnly] - - from_rank : typing.Optional[FromRank] - - to_rank : typing.Optional[ToRank] - - is_headline : typing.Optional[IsHeadline] - - is_opinion : typing.Optional[IsOpinion] - - is_paid_content : typing.Optional[IsPaidContent] - - parent_url : typing.Optional[ParentUrl] - - all_links : typing.Optional[AllLinks] - - all_domain_links : typing.Optional[AllDomainLinks] - - word_count_min : typing.Optional[WordCountMin] - - word_count_max : typing.Optional[WordCountMax] - - page : typing.Optional[Page] - - page_size : typing.Optional[PageSize] - - include_nlp_data : typing.Optional[IncludeNlpData] - - has_nlp : typing.Optional[HasNlp] - - theme : typing.Optional[Theme] - - not_theme : typing.Optional[NotTheme] - - ner_name : typing.Optional[NerName] - - title_sentiment_min : typing.Optional[TitleSentimentMin] - - title_sentiment_max : typing.Optional[TitleSentimentMax] - - content_sentiment_min : typing.Optional[ContentSentimentMin] - - content_sentiment_max : typing.Optional[ContentSentimentMax] - - iptc_tags : typing.Optional[IptcTags] - - not_iptc_tags : typing.Optional[NotIptcTags] - - custom_tags : typing.Optional[CustomTags] - - robots_compliant : typing.Optional[RobotsCompliant] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SearchSimilarPostResponse - A successful response containing articles similar to the specified query. If no matches, returns a failed search response according to the defined schema. - - Examples - -------- - from newscatcher import NewscatcherApi - - client = NewscatcherApi( - api_key="YOUR_API_KEY", - ) - client.searchsimilar.post( - q="artificial intelligence", - include_similar_documents=True, - similar_documents_number=5, - ) - """ - _response = self._raw_client.post( - q=q, - search_in=search_in, - include_translation_fields=include_translation_fields, - include_similar_documents=include_similar_documents, - similar_documents_number=similar_documents_number, - similar_documents_fields=similar_documents_fields, - predefined_sources=predefined_sources, - sources=sources, - not_sources=not_sources, - lang=lang, - not_lang=not_lang, - countries=countries, - not_countries=not_countries, - from_=from_, - to=to, - by_parse_date=by_parse_date, - published_date_precision=published_date_precision, - sort_by=sort_by, - ranked_only=ranked_only, - from_rank=from_rank, - to_rank=to_rank, - is_headline=is_headline, - is_opinion=is_opinion, - is_paid_content=is_paid_content, - parent_url=parent_url, - all_links=all_links, - all_domain_links=all_domain_links, - word_count_min=word_count_min, - word_count_max=word_count_max, - page=page, - page_size=page_size, - include_nlp_data=include_nlp_data, - has_nlp=has_nlp, - theme=theme, - not_theme=not_theme, - ner_name=ner_name, - title_sentiment_min=title_sentiment_min, - title_sentiment_max=title_sentiment_max, - content_sentiment_min=content_sentiment_min, - content_sentiment_max=content_sentiment_max, - iptc_tags=iptc_tags, - not_iptc_tags=not_iptc_tags, - custom_tags=custom_tags, - robots_compliant=robots_compliant, - request_options=request_options, - ) - return _response.data - - -class AsyncSearchsimilarClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawSearchsimilarClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawSearchsimilarClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawSearchsimilarClient - """ - return self._raw_client - - async def get( - self, - *, - q: str, - search_in: typing.Optional[SearchIn] = None, - include_translation_fields: typing.Optional[IncludeTranslationFields] = None, - include_similar_documents: typing.Optional[bool] = None, - similar_documents_number: typing.Optional[int] = None, - similar_documents_fields: typing.Optional[str] = None, - predefined_sources: typing.Optional[str] = None, - sources: typing.Optional[str] = None, - not_sources: typing.Optional[str] = None, - lang: typing.Optional[str] = None, - not_lang: typing.Optional[str] = None, - countries: typing.Optional[str] = None, - not_countries: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - by_parse_date: typing.Optional[bool] = None, - published_date_precision: typing.Optional[SearchSimilarGetRequestPublishedDatePrecision] = None, - sort_by: typing.Optional[SearchSimilarGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, - parent_url: typing.Optional[str] = None, - all_links: typing.Optional[str] = None, - all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - include_nlp_data: typing.Optional[IncludeNlpData] = None, - has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - ner_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, - iptc_tags: typing.Optional[str] = None, - not_iptc_tags: typing.Optional[str] = None, - custom_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SearchSimilarGetResponse: - """ - Searches for articles similar to a specified query. - - Parameters - ---------- - q : str - The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - - - For an exact match, use double quotes. For example, `"technology news"`. - - Use `*` to search for any keyword. - - Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. - - Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - - For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). - - search_in : typing.Optional[SearchIn] - - include_translation_fields : typing.Optional[IncludeTranslationFields] - - include_similar_documents : typing.Optional[bool] - If true, includes similar documents in the response. - - similar_documents_number : typing.Optional[int] - The number of similar documents to return. - - similar_documents_fields : typing.Optional[str] - The fields to consider for finding similar documents. - - predefined_sources : typing.Optional[str] - Predefined top news sources per country. - - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. - - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` - - sources : typing.Optional[str] - One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - - not_sources : typing.Optional[str] - The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - - Example: `"cnn.com, wsj.com"` - - lang : typing.Optional[str] - The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). - - not_lang : typing.Optional[str] - The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). - - countries : typing.Optional[str] - The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). - - not_countries : typing.Optional[str] - The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). - - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. - - published_date_precision : typing.Optional[SearchSimilarGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. - - sort_by : typing.Optional[SearchSimilarGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. - - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. - - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. - - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. - - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. - - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. - - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. - - parent_url : typing.Optional[str] - The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - - Example: `"wsj.com/politics, wsj.com/tech"` - - all_links : typing.Optional[str] - The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). - - all_domain_links : typing.Optional[str] - The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - - Example: `"who.int, nih.gov"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). - - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. - - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. - - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). - - page_size : typing.Optional[int] - The number of articles to return per page. - - include_nlp_data : typing.Optional[IncludeNlpData] - - has_nlp : typing.Optional[HasNlp] - - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - ner_name : typing.Optional[str] - The name of person, organization, location, product or other named entity to search for. To specify multiple names use a comma-separated string. - - Example: `"Tesla, Amazon"` - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - iptc_tags : typing.Optional[str] - Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - - Example: `"20000199, 20000209"` - - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. - - To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). - - not_iptc_tags : typing.Optional[str] - Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - - Example: `"20000205, 20000209"` - - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. - - To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). - - custom_tags : typing.Optional[str] - Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. - - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` - - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). - - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SearchSimilarGetResponse - A successful response containing articles similar to the specified query. If no matches, returns a failed search response according to the defined schema. - - Examples - -------- - import asyncio - import datetime - - from newscatcher import AsyncNewscatcherApi - - client = AsyncNewscatcherApi( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.searchsimilar.get( - q="technology AND (Apple OR Microsoft) NOT Google", - search_in="title_content, title_content_translated", - include_translation_fields=True, - similar_documents_fields="title,summary", - predefined_sources="top 100 US, top 5 GB", - from_=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", - ), - to=datetime.datetime.fromisoformat( - "2024-07-01 00:00:00+00:00", - ), - include_nlp_data=True, - has_nlp=True, - theme="Business,Finance", - not_theme="Crime", - ner_name="Tesla", - iptc_tags="20000199,20000209", - not_iptc_tags="20000205,20000209", - custom_tags="Tag1,Tag2,Tag3", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.get( - q=q, - search_in=search_in, - include_translation_fields=include_translation_fields, - include_similar_documents=include_similar_documents, - similar_documents_number=similar_documents_number, - similar_documents_fields=similar_documents_fields, - predefined_sources=predefined_sources, - sources=sources, - not_sources=not_sources, - lang=lang, - not_lang=not_lang, - countries=countries, - not_countries=not_countries, - from_=from_, - to=to, - by_parse_date=by_parse_date, - published_date_precision=published_date_precision, - sort_by=sort_by, - ranked_only=ranked_only, - from_rank=from_rank, - to_rank=to_rank, - is_headline=is_headline, - is_opinion=is_opinion, - is_paid_content=is_paid_content, - parent_url=parent_url, - all_links=all_links, - all_domain_links=all_domain_links, - word_count_min=word_count_min, - word_count_max=word_count_max, - page=page, - page_size=page_size, - include_nlp_data=include_nlp_data, - has_nlp=has_nlp, - theme=theme, - not_theme=not_theme, - ner_name=ner_name, - title_sentiment_min=title_sentiment_min, - title_sentiment_max=title_sentiment_max, - content_sentiment_min=content_sentiment_min, - content_sentiment_max=content_sentiment_max, - iptc_tags=iptc_tags, - not_iptc_tags=not_iptc_tags, - custom_tags=custom_tags, - robots_compliant=robots_compliant, - request_options=request_options, - ) - return _response.data - - async def post( - self, - *, - q: Q, - search_in: typing.Optional[SearchIn] = OMIT, - include_translation_fields: typing.Optional[IncludeTranslationFields] = OMIT, - include_similar_documents: typing.Optional[IncludeSimilarDocuments] = OMIT, - similar_documents_number: typing.Optional[SimilarDocumentsNumber] = OMIT, - similar_documents_fields: typing.Optional[SimilarDocumentsFields] = OMIT, - predefined_sources: typing.Optional[PredefinedSources] = OMIT, - sources: typing.Optional[Sources] = OMIT, - not_sources: typing.Optional[NotSources] = OMIT, - lang: typing.Optional[Lang] = OMIT, - not_lang: typing.Optional[NotLang] = OMIT, - countries: typing.Optional[Countries] = OMIT, - not_countries: typing.Optional[NotCountries] = OMIT, - from_: typing.Optional[From] = OMIT, - to: typing.Optional[To] = OMIT, - by_parse_date: typing.Optional[ByParseDate] = OMIT, - published_date_precision: typing.Optional[PublishedDatePrecision] = OMIT, - sort_by: typing.Optional[SortBy] = OMIT, - ranked_only: typing.Optional[RankedOnly] = OMIT, - from_rank: typing.Optional[FromRank] = OMIT, - to_rank: typing.Optional[ToRank] = OMIT, - is_headline: typing.Optional[IsHeadline] = OMIT, - is_opinion: typing.Optional[IsOpinion] = OMIT, - is_paid_content: typing.Optional[IsPaidContent] = OMIT, - parent_url: typing.Optional[ParentUrl] = OMIT, - all_links: typing.Optional[AllLinks] = OMIT, - all_domain_links: typing.Optional[AllDomainLinks] = OMIT, - word_count_min: typing.Optional[WordCountMin] = OMIT, - word_count_max: typing.Optional[WordCountMax] = OMIT, - page: typing.Optional[Page] = OMIT, - page_size: typing.Optional[PageSize] = OMIT, - include_nlp_data: typing.Optional[IncludeNlpData] = OMIT, - has_nlp: typing.Optional[HasNlp] = OMIT, - theme: typing.Optional[Theme] = OMIT, - not_theme: typing.Optional[NotTheme] = OMIT, - ner_name: typing.Optional[NerName] = OMIT, - title_sentiment_min: typing.Optional[TitleSentimentMin] = OMIT, - title_sentiment_max: typing.Optional[TitleSentimentMax] = OMIT, - content_sentiment_min: typing.Optional[ContentSentimentMin] = OMIT, - content_sentiment_max: typing.Optional[ContentSentimentMax] = OMIT, - iptc_tags: typing.Optional[IptcTags] = OMIT, - not_iptc_tags: typing.Optional[NotIptcTags] = OMIT, - custom_tags: typing.Optional[CustomTags] = OMIT, - robots_compliant: typing.Optional[RobotsCompliant] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> SearchSimilarPostResponse: - """ - Searches for articles similar to the specified query. You can filter results by language, country, source, and more. - - Parameters - ---------- - q : Q - - search_in : typing.Optional[SearchIn] - - include_translation_fields : typing.Optional[IncludeTranslationFields] - - include_similar_documents : typing.Optional[IncludeSimilarDocuments] - - similar_documents_number : typing.Optional[SimilarDocumentsNumber] - - similar_documents_fields : typing.Optional[SimilarDocumentsFields] - - predefined_sources : typing.Optional[PredefinedSources] - - sources : typing.Optional[Sources] - - not_sources : typing.Optional[NotSources] - - lang : typing.Optional[Lang] - - not_lang : typing.Optional[NotLang] - - countries : typing.Optional[Countries] - - not_countries : typing.Optional[NotCountries] - - from_ : typing.Optional[From] - - to : typing.Optional[To] - - by_parse_date : typing.Optional[ByParseDate] - - published_date_precision : typing.Optional[PublishedDatePrecision] - - sort_by : typing.Optional[SortBy] - - ranked_only : typing.Optional[RankedOnly] - - from_rank : typing.Optional[FromRank] - - to_rank : typing.Optional[ToRank] - - is_headline : typing.Optional[IsHeadline] - - is_opinion : typing.Optional[IsOpinion] - - is_paid_content : typing.Optional[IsPaidContent] - - parent_url : typing.Optional[ParentUrl] - - all_links : typing.Optional[AllLinks] - - all_domain_links : typing.Optional[AllDomainLinks] - - word_count_min : typing.Optional[WordCountMin] - - word_count_max : typing.Optional[WordCountMax] - - page : typing.Optional[Page] - - page_size : typing.Optional[PageSize] - - include_nlp_data : typing.Optional[IncludeNlpData] - - has_nlp : typing.Optional[HasNlp] - - theme : typing.Optional[Theme] - - not_theme : typing.Optional[NotTheme] - - ner_name : typing.Optional[NerName] - - title_sentiment_min : typing.Optional[TitleSentimentMin] - - title_sentiment_max : typing.Optional[TitleSentimentMax] - - content_sentiment_min : typing.Optional[ContentSentimentMin] - - content_sentiment_max : typing.Optional[ContentSentimentMax] - - iptc_tags : typing.Optional[IptcTags] - - not_iptc_tags : typing.Optional[NotIptcTags] - - custom_tags : typing.Optional[CustomTags] - - robots_compliant : typing.Optional[RobotsCompliant] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SearchSimilarPostResponse - A successful response containing articles similar to the specified query. If no matches, returns a failed search response according to the defined schema. - - Examples - -------- - import asyncio - - from newscatcher import AsyncNewscatcherApi - - client = AsyncNewscatcherApi( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.searchsimilar.post( - q="artificial intelligence", - include_similar_documents=True, - similar_documents_number=5, - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.post( - q=q, - search_in=search_in, - include_translation_fields=include_translation_fields, - include_similar_documents=include_similar_documents, - similar_documents_number=similar_documents_number, - similar_documents_fields=similar_documents_fields, - predefined_sources=predefined_sources, - sources=sources, - not_sources=not_sources, - lang=lang, - not_lang=not_lang, - countries=countries, - not_countries=not_countries, - from_=from_, - to=to, - by_parse_date=by_parse_date, - published_date_precision=published_date_precision, - sort_by=sort_by, - ranked_only=ranked_only, - from_rank=from_rank, - to_rank=to_rank, - is_headline=is_headline, - is_opinion=is_opinion, - is_paid_content=is_paid_content, - parent_url=parent_url, - all_links=all_links, - all_domain_links=all_domain_links, - word_count_min=word_count_min, - word_count_max=word_count_max, - page=page, - page_size=page_size, - include_nlp_data=include_nlp_data, - has_nlp=has_nlp, - theme=theme, - not_theme=not_theme, - ner_name=ner_name, - title_sentiment_min=title_sentiment_min, - title_sentiment_max=title_sentiment_max, - content_sentiment_min=content_sentiment_min, - content_sentiment_max=content_sentiment_max, - iptc_tags=iptc_tags, - not_iptc_tags=not_iptc_tags, - custom_tags=custom_tags, - robots_compliant=robots_compliant, - request_options=request_options, - ) - return _response.data diff --git a/src/newscatcher/searchsimilar/raw_client.py b/src/newscatcher/searchsimilar/raw_client.py deleted file mode 100644 index 68c4769..0000000 --- a/src/newscatcher/searchsimilar/raw_client.py +++ /dev/null @@ -1,1684 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing -from json.decoder import JSONDecodeError - -from ..core.api_error import ApiError -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.datetime_utils import serialize_datetime -from ..core.http_response import AsyncHttpResponse, HttpResponse -from ..core.pydantic_utilities import parse_obj_as -from ..core.request_options import RequestOptions -from ..core.serialization import convert_and_respect_annotation_metadata -from ..errors.bad_request_error import BadRequestError -from ..errors.forbidden_error import ForbiddenError -from ..errors.internal_server_error import InternalServerError -from ..errors.request_timeout_error import RequestTimeoutError -from ..errors.too_many_requests_error import TooManyRequestsError -from ..errors.unauthorized_error import UnauthorizedError -from ..errors.unprocessable_entity_error import UnprocessableEntityError -from ..types.all_domain_links import AllDomainLinks -from ..types.all_links import AllLinks -from ..types.by_parse_date import ByParseDate -from ..types.content_sentiment_max import ContentSentimentMax -from ..types.content_sentiment_min import ContentSentimentMin -from ..types.countries import Countries -from ..types.custom_tags import CustomTags -from ..types.error import Error -from ..types.from_ import From -from ..types.from_rank import FromRank -from ..types.has_nlp import HasNlp -from ..types.include_nlp_data import IncludeNlpData -from ..types.include_similar_documents import IncludeSimilarDocuments -from ..types.include_translation_fields import IncludeTranslationFields -from ..types.iptc_tags import IptcTags -from ..types.is_headline import IsHeadline -from ..types.is_opinion import IsOpinion -from ..types.is_paid_content import IsPaidContent -from ..types.lang import Lang -from ..types.ner_name import NerName -from ..types.not_countries import NotCountries -from ..types.not_iptc_tags import NotIptcTags -from ..types.not_lang import NotLang -from ..types.not_sources import NotSources -from ..types.not_theme import NotTheme -from ..types.page import Page -from ..types.page_size import PageSize -from ..types.parent_url import ParentUrl -from ..types.predefined_sources import PredefinedSources -from ..types.published_date_precision import PublishedDatePrecision -from ..types.q import Q -from ..types.ranked_only import RankedOnly -from ..types.robots_compliant import RobotsCompliant -from ..types.search_in import SearchIn -from ..types.similar_documents_fields import SimilarDocumentsFields -from ..types.similar_documents_number import SimilarDocumentsNumber -from ..types.sort_by import SortBy -from ..types.sources import Sources -from ..types.theme import Theme -from ..types.title_sentiment_max import TitleSentimentMax -from ..types.title_sentiment_min import TitleSentimentMin -from ..types.to import To -from ..types.to_rank import ToRank -from ..types.word_count_max import WordCountMax -from ..types.word_count_min import WordCountMin -from .types.search_similar_get_request_published_date_precision import SearchSimilarGetRequestPublishedDatePrecision -from .types.search_similar_get_request_sort_by import SearchSimilarGetRequestSortBy -from .types.search_similar_get_response import SearchSimilarGetResponse -from .types.search_similar_post_response import SearchSimilarPostResponse - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class RawSearchsimilarClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def get( - self, - *, - q: str, - search_in: typing.Optional[SearchIn] = None, - include_translation_fields: typing.Optional[IncludeTranslationFields] = None, - include_similar_documents: typing.Optional[bool] = None, - similar_documents_number: typing.Optional[int] = None, - similar_documents_fields: typing.Optional[str] = None, - predefined_sources: typing.Optional[str] = None, - sources: typing.Optional[str] = None, - not_sources: typing.Optional[str] = None, - lang: typing.Optional[str] = None, - not_lang: typing.Optional[str] = None, - countries: typing.Optional[str] = None, - not_countries: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - by_parse_date: typing.Optional[bool] = None, - published_date_precision: typing.Optional[SearchSimilarGetRequestPublishedDatePrecision] = None, - sort_by: typing.Optional[SearchSimilarGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, - parent_url: typing.Optional[str] = None, - all_links: typing.Optional[str] = None, - all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - include_nlp_data: typing.Optional[IncludeNlpData] = None, - has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - ner_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, - iptc_tags: typing.Optional[str] = None, - not_iptc_tags: typing.Optional[str] = None, - custom_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[SearchSimilarGetResponse]: - """ - Searches for articles similar to a specified query. - - Parameters - ---------- - q : str - The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - - - For an exact match, use double quotes. For example, `"technology news"`. - - Use `*` to search for any keyword. - - Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. - - Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - - For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). - - search_in : typing.Optional[SearchIn] - - include_translation_fields : typing.Optional[IncludeTranslationFields] - - include_similar_documents : typing.Optional[bool] - If true, includes similar documents in the response. - - similar_documents_number : typing.Optional[int] - The number of similar documents to return. - - similar_documents_fields : typing.Optional[str] - The fields to consider for finding similar documents. - - predefined_sources : typing.Optional[str] - Predefined top news sources per country. - - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. - - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` - - sources : typing.Optional[str] - One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - - not_sources : typing.Optional[str] - The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - - Example: `"cnn.com, wsj.com"` - - lang : typing.Optional[str] - The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). - - not_lang : typing.Optional[str] - The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). - - countries : typing.Optional[str] - The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). - - not_countries : typing.Optional[str] - The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). - - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. - - published_date_precision : typing.Optional[SearchSimilarGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. - - sort_by : typing.Optional[SearchSimilarGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. - - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. - - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. - - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. - - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. - - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. - - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. - - parent_url : typing.Optional[str] - The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - - Example: `"wsj.com/politics, wsj.com/tech"` - - all_links : typing.Optional[str] - The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). - - all_domain_links : typing.Optional[str] - The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - - Example: `"who.int, nih.gov"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). - - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. - - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. - - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). - - page_size : typing.Optional[int] - The number of articles to return per page. - - include_nlp_data : typing.Optional[IncludeNlpData] - - has_nlp : typing.Optional[HasNlp] - - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - ner_name : typing.Optional[str] - The name of person, organization, location, product or other named entity to search for. To specify multiple names use a comma-separated string. - - Example: `"Tesla, Amazon"` - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - iptc_tags : typing.Optional[str] - Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - - Example: `"20000199, 20000209"` - - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. - - To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). - - not_iptc_tags : typing.Optional[str] - Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - - Example: `"20000205, 20000209"` - - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. - - To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). - - custom_tags : typing.Optional[str] - Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. - - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` - - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). - - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[SearchSimilarGetResponse] - A successful response containing articles similar to the specified query. If no matches, returns a failed search response according to the defined schema. - """ - _response = self._client_wrapper.httpx_client.request( - "api/search_similar", - method="GET", - params={ - "q": q, - "search_in": search_in, - "include_translation_fields": include_translation_fields, - "include_similar_documents": include_similar_documents, - "similar_documents_number": similar_documents_number, - "similar_documents_fields": similar_documents_fields, - "predefined_sources": predefined_sources, - "sources": sources, - "not_sources": not_sources, - "lang": lang, - "not_lang": not_lang, - "countries": countries, - "not_countries": not_countries, - "from_": serialize_datetime(from_) if from_ is not None else None, - "to_": serialize_datetime(to) if to is not None else None, - "by_parse_date": by_parse_date, - "published_date_precision": published_date_precision, - "sort_by": sort_by, - "ranked_only": ranked_only, - "from_rank": from_rank, - "to_rank": to_rank, - "is_headline": is_headline, - "is_opinion": is_opinion, - "is_paid_content": is_paid_content, - "parent_url": parent_url, - "all_links": all_links, - "all_domain_links": all_domain_links, - "word_count_min": word_count_min, - "word_count_max": word_count_max, - "page": page, - "page_size": page_size, - "include_nlp_data": include_nlp_data, - "has_nlp": has_nlp, - "theme": theme, - "not_theme": not_theme, - "ner_name": ner_name, - "title_sentiment_min": title_sentiment_min, - "title_sentiment_max": title_sentiment_max, - "content_sentiment_min": content_sentiment_min, - "content_sentiment_max": content_sentiment_max, - "iptc_tags": iptc_tags, - "not_iptc_tags": not_iptc_tags, - "custom_tags": custom_tags, - "robots_compliant": robots_compliant, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SearchSimilarGetResponse, - parse_obj_as( - type_=SearchSimilarGetResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - 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 == 408: - raise RequestTimeoutError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 422: - raise UnprocessableEntityError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - str, - parse_obj_as( - type_=str, # 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) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def post( - self, - *, - q: Q, - search_in: typing.Optional[SearchIn] = OMIT, - include_translation_fields: typing.Optional[IncludeTranslationFields] = OMIT, - include_similar_documents: typing.Optional[IncludeSimilarDocuments] = OMIT, - similar_documents_number: typing.Optional[SimilarDocumentsNumber] = OMIT, - similar_documents_fields: typing.Optional[SimilarDocumentsFields] = OMIT, - predefined_sources: typing.Optional[PredefinedSources] = OMIT, - sources: typing.Optional[Sources] = OMIT, - not_sources: typing.Optional[NotSources] = OMIT, - lang: typing.Optional[Lang] = OMIT, - not_lang: typing.Optional[NotLang] = OMIT, - countries: typing.Optional[Countries] = OMIT, - not_countries: typing.Optional[NotCountries] = OMIT, - from_: typing.Optional[From] = OMIT, - to: typing.Optional[To] = OMIT, - by_parse_date: typing.Optional[ByParseDate] = OMIT, - published_date_precision: typing.Optional[PublishedDatePrecision] = OMIT, - sort_by: typing.Optional[SortBy] = OMIT, - ranked_only: typing.Optional[RankedOnly] = OMIT, - from_rank: typing.Optional[FromRank] = OMIT, - to_rank: typing.Optional[ToRank] = OMIT, - is_headline: typing.Optional[IsHeadline] = OMIT, - is_opinion: typing.Optional[IsOpinion] = OMIT, - is_paid_content: typing.Optional[IsPaidContent] = OMIT, - parent_url: typing.Optional[ParentUrl] = OMIT, - all_links: typing.Optional[AllLinks] = OMIT, - all_domain_links: typing.Optional[AllDomainLinks] = OMIT, - word_count_min: typing.Optional[WordCountMin] = OMIT, - word_count_max: typing.Optional[WordCountMax] = OMIT, - page: typing.Optional[Page] = OMIT, - page_size: typing.Optional[PageSize] = OMIT, - include_nlp_data: typing.Optional[IncludeNlpData] = OMIT, - has_nlp: typing.Optional[HasNlp] = OMIT, - theme: typing.Optional[Theme] = OMIT, - not_theme: typing.Optional[NotTheme] = OMIT, - ner_name: typing.Optional[NerName] = OMIT, - title_sentiment_min: typing.Optional[TitleSentimentMin] = OMIT, - title_sentiment_max: typing.Optional[TitleSentimentMax] = OMIT, - content_sentiment_min: typing.Optional[ContentSentimentMin] = OMIT, - content_sentiment_max: typing.Optional[ContentSentimentMax] = OMIT, - iptc_tags: typing.Optional[IptcTags] = OMIT, - not_iptc_tags: typing.Optional[NotIptcTags] = OMIT, - custom_tags: typing.Optional[CustomTags] = OMIT, - robots_compliant: typing.Optional[RobotsCompliant] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[SearchSimilarPostResponse]: - """ - Searches for articles similar to the specified query. You can filter results by language, country, source, and more. - - Parameters - ---------- - q : Q - - search_in : typing.Optional[SearchIn] - - include_translation_fields : typing.Optional[IncludeTranslationFields] - - include_similar_documents : typing.Optional[IncludeSimilarDocuments] - - similar_documents_number : typing.Optional[SimilarDocumentsNumber] - - similar_documents_fields : typing.Optional[SimilarDocumentsFields] - - predefined_sources : typing.Optional[PredefinedSources] - - sources : typing.Optional[Sources] - - not_sources : typing.Optional[NotSources] - - lang : typing.Optional[Lang] - - not_lang : typing.Optional[NotLang] - - countries : typing.Optional[Countries] - - not_countries : typing.Optional[NotCountries] - - from_ : typing.Optional[From] - - to : typing.Optional[To] - - by_parse_date : typing.Optional[ByParseDate] - - published_date_precision : typing.Optional[PublishedDatePrecision] - - sort_by : typing.Optional[SortBy] - - ranked_only : typing.Optional[RankedOnly] - - from_rank : typing.Optional[FromRank] - - to_rank : typing.Optional[ToRank] - - is_headline : typing.Optional[IsHeadline] - - is_opinion : typing.Optional[IsOpinion] - - is_paid_content : typing.Optional[IsPaidContent] - - parent_url : typing.Optional[ParentUrl] - - all_links : typing.Optional[AllLinks] - - all_domain_links : typing.Optional[AllDomainLinks] - - word_count_min : typing.Optional[WordCountMin] - - word_count_max : typing.Optional[WordCountMax] - - page : typing.Optional[Page] - - page_size : typing.Optional[PageSize] - - include_nlp_data : typing.Optional[IncludeNlpData] - - has_nlp : typing.Optional[HasNlp] - - theme : typing.Optional[Theme] - - not_theme : typing.Optional[NotTheme] - - ner_name : typing.Optional[NerName] - - title_sentiment_min : typing.Optional[TitleSentimentMin] - - title_sentiment_max : typing.Optional[TitleSentimentMax] - - content_sentiment_min : typing.Optional[ContentSentimentMin] - - content_sentiment_max : typing.Optional[ContentSentimentMax] - - iptc_tags : typing.Optional[IptcTags] - - not_iptc_tags : typing.Optional[NotIptcTags] - - custom_tags : typing.Optional[CustomTags] - - robots_compliant : typing.Optional[RobotsCompliant] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[SearchSimilarPostResponse] - A successful response containing articles similar to the specified query. If no matches, returns a failed search response according to the defined schema. - """ - _response = self._client_wrapper.httpx_client.request( - "api/search_similar", - method="POST", - json={ - "q": q, - "search_in": search_in, - "include_translation_fields": include_translation_fields, - "include_similar_documents": include_similar_documents, - "similar_documents_number": similar_documents_number, - "similar_documents_fields": similar_documents_fields, - "predefined_sources": convert_and_respect_annotation_metadata( - object_=predefined_sources, annotation=PredefinedSources, direction="write" - ), - "sources": convert_and_respect_annotation_metadata( - object_=sources, annotation=Sources, direction="write" - ), - "not_sources": convert_and_respect_annotation_metadata( - object_=not_sources, annotation=NotSources, direction="write" - ), - "lang": convert_and_respect_annotation_metadata(object_=lang, annotation=Lang, direction="write"), - "not_lang": convert_and_respect_annotation_metadata( - object_=not_lang, annotation=NotLang, direction="write" - ), - "countries": convert_and_respect_annotation_metadata( - object_=countries, annotation=Countries, direction="write" - ), - "not_countries": convert_and_respect_annotation_metadata( - object_=not_countries, annotation=NotCountries, direction="write" - ), - "from_": convert_and_respect_annotation_metadata(object_=from_, annotation=From, direction="write"), - "to_": convert_and_respect_annotation_metadata(object_=to, annotation=To, direction="write"), - "by_parse_date": by_parse_date, - "published_date_precision": published_date_precision, - "sort_by": sort_by, - "ranked_only": ranked_only, - "from_rank": from_rank, - "to_rank": to_rank, - "is_headline": is_headline, - "is_opinion": is_opinion, - "is_paid_content": is_paid_content, - "parent_url": convert_and_respect_annotation_metadata( - object_=parent_url, annotation=ParentUrl, direction="write" - ), - "all_links": convert_and_respect_annotation_metadata( - object_=all_links, annotation=AllLinks, direction="write" - ), - "all_domain_links": convert_and_respect_annotation_metadata( - object_=all_domain_links, annotation=AllDomainLinks, direction="write" - ), - "word_count_min": word_count_min, - "word_count_max": word_count_max, - "page": page, - "page_size": page_size, - "include_nlp_data": include_nlp_data, - "has_nlp": has_nlp, - "theme": convert_and_respect_annotation_metadata(object_=theme, annotation=Theme, direction="write"), - "not_theme": convert_and_respect_annotation_metadata( - object_=not_theme, annotation=NotTheme, direction="write" - ), - "ner_name": ner_name, - "title_sentiment_min": title_sentiment_min, - "title_sentiment_max": title_sentiment_max, - "content_sentiment_min": content_sentiment_min, - "content_sentiment_max": content_sentiment_max, - "iptc_tags": convert_and_respect_annotation_metadata( - object_=iptc_tags, annotation=IptcTags, direction="write" - ), - "not_iptc_tags": convert_and_respect_annotation_metadata( - object_=not_iptc_tags, annotation=NotIptcTags, direction="write" - ), - "custom_tags": convert_and_respect_annotation_metadata( - object_=custom_tags, annotation=CustomTags, direction="write" - ), - "robots_compliant": robots_compliant, - }, - headers={ - "content-type": "application/json", - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SearchSimilarPostResponse, - parse_obj_as( - type_=SearchSimilarPostResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - 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 == 408: - raise RequestTimeoutError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 422: - raise UnprocessableEntityError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - str, - parse_obj_as( - type_=str, # 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) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawSearchsimilarClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def get( - self, - *, - q: str, - search_in: typing.Optional[SearchIn] = None, - include_translation_fields: typing.Optional[IncludeTranslationFields] = None, - include_similar_documents: typing.Optional[bool] = None, - similar_documents_number: typing.Optional[int] = None, - similar_documents_fields: typing.Optional[str] = None, - predefined_sources: typing.Optional[str] = None, - sources: typing.Optional[str] = None, - not_sources: typing.Optional[str] = None, - lang: typing.Optional[str] = None, - not_lang: typing.Optional[str] = None, - countries: typing.Optional[str] = None, - not_countries: typing.Optional[str] = None, - from_: typing.Optional[dt.datetime] = None, - to: typing.Optional[dt.datetime] = None, - by_parse_date: typing.Optional[bool] = None, - published_date_precision: typing.Optional[SearchSimilarGetRequestPublishedDatePrecision] = None, - sort_by: typing.Optional[SearchSimilarGetRequestSortBy] = None, - ranked_only: typing.Optional[bool] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, - is_headline: typing.Optional[bool] = None, - is_opinion: typing.Optional[bool] = None, - is_paid_content: typing.Optional[bool] = None, - parent_url: typing.Optional[str] = None, - all_links: typing.Optional[str] = None, - all_domain_links: typing.Optional[str] = None, - word_count_min: typing.Optional[int] = None, - word_count_max: typing.Optional[int] = None, - page: typing.Optional[int] = None, - page_size: typing.Optional[int] = None, - include_nlp_data: typing.Optional[IncludeNlpData] = None, - has_nlp: typing.Optional[HasNlp] = None, - theme: typing.Optional[str] = None, - not_theme: typing.Optional[str] = None, - ner_name: typing.Optional[str] = None, - title_sentiment_min: typing.Optional[float] = None, - title_sentiment_max: typing.Optional[float] = None, - content_sentiment_min: typing.Optional[float] = None, - content_sentiment_max: typing.Optional[float] = None, - iptc_tags: typing.Optional[str] = None, - not_iptc_tags: typing.Optional[str] = None, - custom_tags: typing.Optional[str] = None, - robots_compliant: typing.Optional[bool] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[SearchSimilarGetResponse]: - """ - Searches for articles similar to a specified query. - - Parameters - ---------- - q : str - The keyword(s) to search for in articles. Query syntax supports logical operators (`AND`, `OR`, `NOT`) and wildcards: - - - For an exact match, use double quotes. For example, `"technology news"`. - - Use `*` to search for any keyword. - - Use `+` to include and `-` to exclude specific words or phrases. - For example, `+Apple`, `-Google`. - - Use `AND`, `OR`, and `NOT` to refine search results. - For example, `technology AND (Apple OR Microsoft) NOT Google`. - - For more details, see [Advanced querying](/docs/v3/documentation/guides-and-concepts/advanced-querying). - - search_in : typing.Optional[SearchIn] - - include_translation_fields : typing.Optional[IncludeTranslationFields] - - include_similar_documents : typing.Optional[bool] - If true, includes similar documents in the response. - - similar_documents_number : typing.Optional[int] - The number of similar documents to return. - - similar_documents_fields : typing.Optional[str] - The fields to consider for finding similar documents. - - predefined_sources : typing.Optional[str] - Predefined top news sources per country. - - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. - - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` - - sources : typing.Optional[str] - One or more news sources to narrow down the search. The format must be a domain URL. Subdomains, such as `finance.yahoo.com`, are also acceptable.To specify multiple sources, use a comma-separated string. - - Examples: - - `"nytimes.com"` - - `"theguardian.com, finance.yahoo.com"` - - not_sources : typing.Optional[str] - The news sources to exclude from the search. To exclude multiple sources, use a comma-separated string. - - Example: `"cnn.com, wsj.com"` - - lang : typing.Optional[str] - The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). - - not_lang : typing.Optional[str] - The language(s) to exclude from the search. The accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To exclude multiple languages, use a comma-separated string. - - Example: `"fr, de"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). - - countries : typing.Optional[str] - The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). - - not_countries : typing.Optional[str] - The publisher location countries to exclude from the search. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To exclude multiple countries, use a comma-separated string. - - Example:`"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). - - from_ : typing.Optional[dt.datetime] - The starting point in time to search from. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `7 day ago`, `today` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - to : typing.Optional[dt.datetime] - The ending point in time to search up to. Accepts date-time strings in ISO 8601 format and plain text. The default time zone is UTC. - - Formats with examples: - - YYYY-mm-ddTHH:MM:SS: `2024-07-01T00:00:00` - - YYYY-MM-dd: `2024-07-01` - - YYYY/mm/dd HH:MM:SS: `2024/07/01 00:00:00` - - YYYY/mm/dd: `2024/07/01` - - English phrases: `1 day ago`, `now` - - **Note**: By default, applied to the publication date of the article. To use the article's parse date instead, set the `by_parse_date` parameter to `true`. - - by_parse_date : typing.Optional[bool] - If true, the `from_` and `to_` parameters use article parse dates instead of published dates. Additionally, the `parse_date` variable is added to the output for each article object. - - published_date_precision : typing.Optional[SearchSimilarGetRequestPublishedDatePrecision] - The precision of the published date. There are three types: - - `full`: The day and time of an article is correctly identified with the appropriate timezone. - - `timezone unknown`: The day and time of an article is correctly identified without timezone. - - `date`: Only the day is identified without an exact time. - - sort_by : typing.Optional[SearchSimilarGetRequestSortBy] - The sorting order of the results. Possible values are: - - `relevancy`: The most relevant results first. - - `date`: The most recently published results first. - - `rank`: The results from the highest-ranked sources first. - - ranked_only : typing.Optional[bool] - If true, limits the search to sources ranked in the top 1 million online websites. If false, includes unranked sources which are assigned a rank of 999999. - - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. - - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. - - is_headline : typing.Optional[bool] - If true, only returns articles that were posted on the home page of a given news domain. - - is_opinion : typing.Optional[bool] - If true, returns only opinion pieces. If false, excludes opinion-based articles and returns news only. - - is_paid_content : typing.Optional[bool] - If false, returns only articles that have publicly available complete content. Some publishers partially block content, so this setting ensures that only full articles are retrieved. - - parent_url : typing.Optional[str] - The categorical URL(s) to filter your search. To filter your search by multiple categorical URLs, use a comma-separated string. - - Example: `"wsj.com/politics, wsj.com/tech"` - - all_links : typing.Optional[str] - The complete URL(s) mentioned in the article. For multiple URLs, use a comma-separated string. - - Example: `"https://aiindex.stanford.edu/report, https://www.stateof.ai"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). - - all_domain_links : typing.Optional[str] - The domain(s) mentioned in the article. For multiple domains, use a comma-separated string. - - Example: `"who.int, nih.gov"` - - For more details, see [Search by URL](/docs/v3/documentation/how-to/search-by-url). - - word_count_min : typing.Optional[int] - The minimum number of words an article must contain. To be used for avoiding articles with small content. - - word_count_max : typing.Optional[int] - The maximum number of words an article can contain. To be used for avoiding articles with large content. - - page : typing.Optional[int] - The page number to scroll through the results. Use for pagination, as a single API response can return up to 1,000 articles. - - For details, see [How to paginate large datasets](https://www.newscatcherapi.com/docs/v3/documentation/how-to/paginate-large-datasets). - - page_size : typing.Optional[int] - The number of articles to return per page. - - include_nlp_data : typing.Optional[IncludeNlpData] - - has_nlp : typing.Optional[HasNlp] - - theme : typing.Optional[str] - Filters articles based on their general topic, as determined by NLP analysis. To select multiple themes, use a comma-separated string. - - Example: `"Finance, Tech"` - - **Note**: The `theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - Available options: `Business`, `Economics`, `Entertainment`, `Finance`, `Health`, `Politics`, `Science`, `Sports`, `Tech`, `Crime`, `Financial Crime`, `Lifestyle`, `Automotive`, `Travel`, `Weather`, `General`. - - not_theme : typing.Optional[str] - Inverse of the `theme` parameter. Excludes articles based on their general topic, as determined by NLP analysis. To exclude multiple themes, use a comma-separated string. - - Example: `"Crime, Tech"` - - **Note**: The `not_theme` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - ner_name : typing.Optional[str] - The name of person, organization, location, product or other named entity to search for. To specify multiple names use a comma-separated string. - - Example: `"Tesla, Amazon"` - - title_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - title_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their titles. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `title_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - content_sentiment_min : typing.Optional[float] - Filters articles based on the minimum sentiment score of their content. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `content_sentiment_min` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - content_sentiment_max : typing.Optional[float] - Filters articles based on the maximum sentiment score of their content. - - Range is `-1.0` to `1.0`, where: - - Negative values indicate negative sentiment. - - Positive values indicate positive sentiment. - - Values close to 0 indicate neutral sentiment. - - **Note**: The `content_sentiment_max` parameter is only available if NLP is included in your subscription plan. - - To learn more, see [NLP features](/docs/v3/documentation/guides-and-concepts/nlp-features). - - iptc_tags : typing.Optional[str] - Filters articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags, use a comma-separated string of tag IDs. - - Example: `"20000199, 20000209"` - - **Note**: The `iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. - - To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). - - not_iptc_tags : typing.Optional[str] - Inverse of the `iptc_tags` parameter. Excludes articles based on International Press Telecommunications Council (IPTC) media topic tags. To specify multiple IPTC tags to exclude, use a comma-separated string of tag IDs. - - Example: `"20000205, 20000209"` - - **Note**: The `not_iptc_tags` parameter is only available in the `v3_nlp_iptc_tags` subscription plan. - - To learn more, see [IPTC Media Topic NewsCodes](https://www.iptc.org/std/NewsCodes/treeview/mediatopic/mediatopic-en-GB.html). - - custom_tags : typing.Optional[str] - Filters articles based on provided taxonomy that is tailored to your specific needs and is accessible only with your API key. To specify tags, use the following pattern: - - - `custom_tags.taxonomy=Tag1,Tag2,Tag3`, where `taxonomy` is the taxonomy name and `Tag1,Tag2,Tag3` is a comma-separated list of tags. - - Example: `custom_tags.industry="Manufacturing, Supply Chain, Logistics"` - - To learn more, see the [Custom tags](/docs/v3/documentation/guides-and-concepts/custom-tags). - - robots_compliant : typing.Optional[bool] - If true, returns only articles/sources that comply with the publisher's robots.txt rules. If false, returns only articles/sources that do not comply with robots.txt rules. If omitted, returns all articles/sources regardless of compliance status. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[SearchSimilarGetResponse] - A successful response containing articles similar to the specified query. If no matches, returns a failed search response according to the defined schema. - """ - _response = await self._client_wrapper.httpx_client.request( - "api/search_similar", - method="GET", - params={ - "q": q, - "search_in": search_in, - "include_translation_fields": include_translation_fields, - "include_similar_documents": include_similar_documents, - "similar_documents_number": similar_documents_number, - "similar_documents_fields": similar_documents_fields, - "predefined_sources": predefined_sources, - "sources": sources, - "not_sources": not_sources, - "lang": lang, - "not_lang": not_lang, - "countries": countries, - "not_countries": not_countries, - "from_": serialize_datetime(from_) if from_ is not None else None, - "to_": serialize_datetime(to) if to is not None else None, - "by_parse_date": by_parse_date, - "published_date_precision": published_date_precision, - "sort_by": sort_by, - "ranked_only": ranked_only, - "from_rank": from_rank, - "to_rank": to_rank, - "is_headline": is_headline, - "is_opinion": is_opinion, - "is_paid_content": is_paid_content, - "parent_url": parent_url, - "all_links": all_links, - "all_domain_links": all_domain_links, - "word_count_min": word_count_min, - "word_count_max": word_count_max, - "page": page, - "page_size": page_size, - "include_nlp_data": include_nlp_data, - "has_nlp": has_nlp, - "theme": theme, - "not_theme": not_theme, - "ner_name": ner_name, - "title_sentiment_min": title_sentiment_min, - "title_sentiment_max": title_sentiment_max, - "content_sentiment_min": content_sentiment_min, - "content_sentiment_max": content_sentiment_max, - "iptc_tags": iptc_tags, - "not_iptc_tags": not_iptc_tags, - "custom_tags": custom_tags, - "robots_compliant": robots_compliant, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SearchSimilarGetResponse, - parse_obj_as( - type_=SearchSimilarGetResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - 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 == 408: - raise RequestTimeoutError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 422: - raise UnprocessableEntityError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - str, - parse_obj_as( - type_=str, # 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) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def post( - self, - *, - q: Q, - search_in: typing.Optional[SearchIn] = OMIT, - include_translation_fields: typing.Optional[IncludeTranslationFields] = OMIT, - include_similar_documents: typing.Optional[IncludeSimilarDocuments] = OMIT, - similar_documents_number: typing.Optional[SimilarDocumentsNumber] = OMIT, - similar_documents_fields: typing.Optional[SimilarDocumentsFields] = OMIT, - predefined_sources: typing.Optional[PredefinedSources] = OMIT, - sources: typing.Optional[Sources] = OMIT, - not_sources: typing.Optional[NotSources] = OMIT, - lang: typing.Optional[Lang] = OMIT, - not_lang: typing.Optional[NotLang] = OMIT, - countries: typing.Optional[Countries] = OMIT, - not_countries: typing.Optional[NotCountries] = OMIT, - from_: typing.Optional[From] = OMIT, - to: typing.Optional[To] = OMIT, - by_parse_date: typing.Optional[ByParseDate] = OMIT, - published_date_precision: typing.Optional[PublishedDatePrecision] = OMIT, - sort_by: typing.Optional[SortBy] = OMIT, - ranked_only: typing.Optional[RankedOnly] = OMIT, - from_rank: typing.Optional[FromRank] = OMIT, - to_rank: typing.Optional[ToRank] = OMIT, - is_headline: typing.Optional[IsHeadline] = OMIT, - is_opinion: typing.Optional[IsOpinion] = OMIT, - is_paid_content: typing.Optional[IsPaidContent] = OMIT, - parent_url: typing.Optional[ParentUrl] = OMIT, - all_links: typing.Optional[AllLinks] = OMIT, - all_domain_links: typing.Optional[AllDomainLinks] = OMIT, - word_count_min: typing.Optional[WordCountMin] = OMIT, - word_count_max: typing.Optional[WordCountMax] = OMIT, - page: typing.Optional[Page] = OMIT, - page_size: typing.Optional[PageSize] = OMIT, - include_nlp_data: typing.Optional[IncludeNlpData] = OMIT, - has_nlp: typing.Optional[HasNlp] = OMIT, - theme: typing.Optional[Theme] = OMIT, - not_theme: typing.Optional[NotTheme] = OMIT, - ner_name: typing.Optional[NerName] = OMIT, - title_sentiment_min: typing.Optional[TitleSentimentMin] = OMIT, - title_sentiment_max: typing.Optional[TitleSentimentMax] = OMIT, - content_sentiment_min: typing.Optional[ContentSentimentMin] = OMIT, - content_sentiment_max: typing.Optional[ContentSentimentMax] = OMIT, - iptc_tags: typing.Optional[IptcTags] = OMIT, - not_iptc_tags: typing.Optional[NotIptcTags] = OMIT, - custom_tags: typing.Optional[CustomTags] = OMIT, - robots_compliant: typing.Optional[RobotsCompliant] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[SearchSimilarPostResponse]: - """ - Searches for articles similar to the specified query. You can filter results by language, country, source, and more. - - Parameters - ---------- - q : Q - - search_in : typing.Optional[SearchIn] - - include_translation_fields : typing.Optional[IncludeTranslationFields] - - include_similar_documents : typing.Optional[IncludeSimilarDocuments] - - similar_documents_number : typing.Optional[SimilarDocumentsNumber] - - similar_documents_fields : typing.Optional[SimilarDocumentsFields] - - predefined_sources : typing.Optional[PredefinedSources] - - sources : typing.Optional[Sources] - - not_sources : typing.Optional[NotSources] - - lang : typing.Optional[Lang] - - not_lang : typing.Optional[NotLang] - - countries : typing.Optional[Countries] - - not_countries : typing.Optional[NotCountries] - - from_ : typing.Optional[From] - - to : typing.Optional[To] - - by_parse_date : typing.Optional[ByParseDate] - - published_date_precision : typing.Optional[PublishedDatePrecision] - - sort_by : typing.Optional[SortBy] - - ranked_only : typing.Optional[RankedOnly] - - from_rank : typing.Optional[FromRank] - - to_rank : typing.Optional[ToRank] - - is_headline : typing.Optional[IsHeadline] - - is_opinion : typing.Optional[IsOpinion] - - is_paid_content : typing.Optional[IsPaidContent] - - parent_url : typing.Optional[ParentUrl] - - all_links : typing.Optional[AllLinks] - - all_domain_links : typing.Optional[AllDomainLinks] - - word_count_min : typing.Optional[WordCountMin] - - word_count_max : typing.Optional[WordCountMax] - - page : typing.Optional[Page] - - page_size : typing.Optional[PageSize] - - include_nlp_data : typing.Optional[IncludeNlpData] - - has_nlp : typing.Optional[HasNlp] - - theme : typing.Optional[Theme] - - not_theme : typing.Optional[NotTheme] - - ner_name : typing.Optional[NerName] - - title_sentiment_min : typing.Optional[TitleSentimentMin] - - title_sentiment_max : typing.Optional[TitleSentimentMax] - - content_sentiment_min : typing.Optional[ContentSentimentMin] - - content_sentiment_max : typing.Optional[ContentSentimentMax] - - iptc_tags : typing.Optional[IptcTags] - - not_iptc_tags : typing.Optional[NotIptcTags] - - custom_tags : typing.Optional[CustomTags] - - robots_compliant : typing.Optional[RobotsCompliant] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[SearchSimilarPostResponse] - A successful response containing articles similar to the specified query. If no matches, returns a failed search response according to the defined schema. - """ - _response = await self._client_wrapper.httpx_client.request( - "api/search_similar", - method="POST", - json={ - "q": q, - "search_in": search_in, - "include_translation_fields": include_translation_fields, - "include_similar_documents": include_similar_documents, - "similar_documents_number": similar_documents_number, - "similar_documents_fields": similar_documents_fields, - "predefined_sources": convert_and_respect_annotation_metadata( - object_=predefined_sources, annotation=PredefinedSources, direction="write" - ), - "sources": convert_and_respect_annotation_metadata( - object_=sources, annotation=Sources, direction="write" - ), - "not_sources": convert_and_respect_annotation_metadata( - object_=not_sources, annotation=NotSources, direction="write" - ), - "lang": convert_and_respect_annotation_metadata(object_=lang, annotation=Lang, direction="write"), - "not_lang": convert_and_respect_annotation_metadata( - object_=not_lang, annotation=NotLang, direction="write" - ), - "countries": convert_and_respect_annotation_metadata( - object_=countries, annotation=Countries, direction="write" - ), - "not_countries": convert_and_respect_annotation_metadata( - object_=not_countries, annotation=NotCountries, direction="write" - ), - "from_": convert_and_respect_annotation_metadata(object_=from_, annotation=From, direction="write"), - "to_": convert_and_respect_annotation_metadata(object_=to, annotation=To, direction="write"), - "by_parse_date": by_parse_date, - "published_date_precision": published_date_precision, - "sort_by": sort_by, - "ranked_only": ranked_only, - "from_rank": from_rank, - "to_rank": to_rank, - "is_headline": is_headline, - "is_opinion": is_opinion, - "is_paid_content": is_paid_content, - "parent_url": convert_and_respect_annotation_metadata( - object_=parent_url, annotation=ParentUrl, direction="write" - ), - "all_links": convert_and_respect_annotation_metadata( - object_=all_links, annotation=AllLinks, direction="write" - ), - "all_domain_links": convert_and_respect_annotation_metadata( - object_=all_domain_links, annotation=AllDomainLinks, direction="write" - ), - "word_count_min": word_count_min, - "word_count_max": word_count_max, - "page": page, - "page_size": page_size, - "include_nlp_data": include_nlp_data, - "has_nlp": has_nlp, - "theme": convert_and_respect_annotation_metadata(object_=theme, annotation=Theme, direction="write"), - "not_theme": convert_and_respect_annotation_metadata( - object_=not_theme, annotation=NotTheme, direction="write" - ), - "ner_name": ner_name, - "title_sentiment_min": title_sentiment_min, - "title_sentiment_max": title_sentiment_max, - "content_sentiment_min": content_sentiment_min, - "content_sentiment_max": content_sentiment_max, - "iptc_tags": convert_and_respect_annotation_metadata( - object_=iptc_tags, annotation=IptcTags, direction="write" - ), - "not_iptc_tags": convert_and_respect_annotation_metadata( - object_=not_iptc_tags, annotation=NotIptcTags, direction="write" - ), - "custom_tags": convert_and_respect_annotation_metadata( - object_=custom_tags, annotation=CustomTags, direction="write" - ), - "robots_compliant": robots_compliant, - }, - headers={ - "content-type": "application/json", - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SearchSimilarPostResponse, - parse_obj_as( - type_=SearchSimilarPostResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - 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 == 408: - raise RequestTimeoutError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 422: - raise UnprocessableEntityError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - str, - parse_obj_as( - type_=str, # 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) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/newscatcher/searchsimilar/types/__init__.py b/src/newscatcher/searchsimilar/types/__init__.py deleted file mode 100644 index 5992030..0000000 --- a/src/newscatcher/searchsimilar/types/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -from .search_similar_get_request_published_date_precision import SearchSimilarGetRequestPublishedDatePrecision -from .search_similar_get_request_sort_by import SearchSimilarGetRequestSortBy -from .search_similar_get_response import SearchSimilarGetResponse -from .search_similar_post_response import SearchSimilarPostResponse - -__all__ = [ - "SearchSimilarGetRequestPublishedDatePrecision", - "SearchSimilarGetRequestSortBy", - "SearchSimilarGetResponse", - "SearchSimilarPostResponse", -] diff --git a/src/newscatcher/searchsimilar/types/search_similar_get_request_published_date_precision.py b/src/newscatcher/searchsimilar/types/search_similar_get_request_published_date_precision.py deleted file mode 100644 index ede9be1..0000000 --- a/src/newscatcher/searchsimilar/types/search_similar_get_request_published_date_precision.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -SearchSimilarGetRequestPublishedDatePrecision = typing.Union[ - typing.Literal["full", "timezone unknown", "date"], typing.Any -] diff --git a/src/newscatcher/searchsimilar/types/search_similar_get_request_sort_by.py b/src/newscatcher/searchsimilar/types/search_similar_get_request_sort_by.py deleted file mode 100644 index 5859da0..0000000 --- a/src/newscatcher/searchsimilar/types/search_similar_get_request_sort_by.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -SearchSimilarGetRequestSortBy = typing.Union[typing.Literal["relevancy", "date", "rank"], typing.Any] diff --git a/src/newscatcher/searchsimilar/types/search_similar_get_response.py b/src/newscatcher/searchsimilar/types/search_similar_get_response.py deleted file mode 100644 index 9815eea..0000000 --- a/src/newscatcher/searchsimilar/types/search_similar_get_response.py +++ /dev/null @@ -1,8 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...types.failed_search_similar_response_dto import FailedSearchSimilarResponseDto -from ...types.search_similar_response_dto import SearchSimilarResponseDto - -SearchSimilarGetResponse = typing.Union[SearchSimilarResponseDto, FailedSearchSimilarResponseDto] diff --git a/src/newscatcher/searchsimilar/types/search_similar_post_response.py b/src/newscatcher/searchsimilar/types/search_similar_post_response.py deleted file mode 100644 index 9fa29f4..0000000 --- a/src/newscatcher/searchsimilar/types/search_similar_post_response.py +++ /dev/null @@ -1,8 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...types.failed_search_similar_response_dto import FailedSearchSimilarResponseDto -from ...types.search_similar_response_dto import SearchSimilarResponseDto - -SearchSimilarPostResponse = typing.Union[SearchSimilarResponseDto, FailedSearchSimilarResponseDto] diff --git a/src/newscatcher/sources/__init__.py b/src/newscatcher/sources/__init__.py index 6f0dd5f..5cde020 100644 --- a/src/newscatcher/sources/__init__.py +++ b/src/newscatcher/sources/__init__.py @@ -2,6 +2,3 @@ # isort: skip_file -from .types import SourcesGetRequestNewsDomainType - -__all__ = ["SourcesGetRequestNewsDomainType"] diff --git a/src/newscatcher/sources/client.py b/src/newscatcher/sources/client.py index 3430e18..70484c5 100644 --- a/src/newscatcher/sources/client.py +++ b/src/newscatcher/sources/client.py @@ -17,7 +17,6 @@ from ..types.sources_response_dto import SourcesResponseDto from ..types.to_rank import ToRank from .raw_client import AsyncRawSourcesClient, RawSourcesClient -from .types.sources_get_request_news_domain_type import SourcesGetRequestNewsDomainType # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -46,12 +45,12 @@ def get( predefined_sources: typing.Optional[str] = None, source_name: typing.Optional[str] = None, source_url: typing.Optional[str] = None, - include_additional_info: typing.Optional[bool] = None, - is_news_domain: typing.Optional[bool] = None, - news_domain_type: typing.Optional[SourcesGetRequestNewsDomainType] = None, + include_additional_info: typing.Optional[IncludeAdditionalInfo] = None, + is_news_domain: typing.Optional[IsNewsDomain] = None, + news_domain_type: typing.Optional[NewsDomainType] = None, news_type: typing.Optional[str] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, request_options: typing.Optional[RequestOptions] = None, ) -> SourcesResponseDto: """ @@ -62,33 +61,23 @@ def get( lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. source_name : typing.Optional[str] Word or phrase to search within the source names. To specify multiple values, use a comma-separated string. - Example: `"sport, tech"` - **Note**: The search doesn't require an exact match and returns sources containing the specified terms in their names. You can use any word or phrase, like `"sport"` or `"new york times"`. For example, `"sport"` returns sources such as `"Motorsport"`, `"Dot Esport"`, and `"Tuttosport"`. source_url : typing.Optional[str] @@ -97,38 +86,20 @@ def get( **Caution**: When specifying the `source_url` parameter, you can only use `include_additional_info` as an extra parameter. - include_additional_info : typing.Optional[bool] - If true, returns the following additional datapoints about each news source: - - `nb_articles_for_7d`: The number of articles published by the source in the last week. - - `country`: Source country of origin. - - `rank`: SEO rank. - - `is_news_domain`: Boolean indicating if the source is a news domain. - - `news_domain_type`: Type of news domain (e.g., "Original Content"). - - `news_type`: Category of news (e.g., "General News Outlets"). - - is_news_domain : typing.Optional[bool] - If true, filters results to include only news domains. - - news_domain_type : typing.Optional[SourcesGetRequestNewsDomainType] - Filters results based on the news domain type. Possible values are: - - `Original Content`: Sources that produce their own content. - - `Aggregator`: Sources that collect content from various other sources. - - `Press Releases`: Sources primarily publishing press releases. - - `Republisher`: Sources that republish content from other sources. - - `Other`: Sources that don't fit into main categories. + include_additional_info : typing.Optional[IncludeAdditionalInfo] + + is_news_domain : typing.Optional[IsNewsDomain] + + news_domain_type : typing.Optional[NewsDomainType] news_type : typing.Optional[str] Filters results based on the news type. Multiple types can be specified using a comma-separated string. - Example: `"General News Outlets,Tech News and Updates"` + For a complete list of available news types, see [Enumerated parameters > News type](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#news-type-news-type). - For a complete list of available news types, see [Enumerated parameters > News type](/docs/v3/api-reference/overview/enumerated-parameters#news-type-news-type). - - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -146,8 +117,16 @@ def get( api_key="YOUR_API_KEY", ) client.sources.get( - predefined_sources="top 100 US, top 5 GB", + lang="en,es", + countries="US,CA", + predefined_sources="top 50 US, top 20 GB", + source_name="sport,tech", source_url="bbc.com", + include_additional_info=True, + is_news_domain=True, + news_type="General News Outlets,Tech News and Updates", + from_rank=100, + to_rank=100, ) """ _response = self._raw_client.get( @@ -225,11 +204,7 @@ def post( api_key="YOUR_API_KEY", ) client.sources.post( - predefined_sources=["top 50 US"], - include_additional_info=True, - is_news_domain=True, - news_domain_type="Original Content", - news_type="General News Outlets", + predefined_sources="top 10 US", ) """ _response = self._raw_client.post( @@ -272,12 +247,12 @@ async def get( predefined_sources: typing.Optional[str] = None, source_name: typing.Optional[str] = None, source_url: typing.Optional[str] = None, - include_additional_info: typing.Optional[bool] = None, - is_news_domain: typing.Optional[bool] = None, - news_domain_type: typing.Optional[SourcesGetRequestNewsDomainType] = None, + include_additional_info: typing.Optional[IncludeAdditionalInfo] = None, + is_news_domain: typing.Optional[IsNewsDomain] = None, + news_domain_type: typing.Optional[NewsDomainType] = None, news_type: typing.Optional[str] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, request_options: typing.Optional[RequestOptions] = None, ) -> SourcesResponseDto: """ @@ -288,33 +263,23 @@ async def get( lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. source_name : typing.Optional[str] Word or phrase to search within the source names. To specify multiple values, use a comma-separated string. - Example: `"sport, tech"` - **Note**: The search doesn't require an exact match and returns sources containing the specified terms in their names. You can use any word or phrase, like `"sport"` or `"new york times"`. For example, `"sport"` returns sources such as `"Motorsport"`, `"Dot Esport"`, and `"Tuttosport"`. source_url : typing.Optional[str] @@ -323,38 +288,20 @@ async def get( **Caution**: When specifying the `source_url` parameter, you can only use `include_additional_info` as an extra parameter. - include_additional_info : typing.Optional[bool] - If true, returns the following additional datapoints about each news source: - - `nb_articles_for_7d`: The number of articles published by the source in the last week. - - `country`: Source country of origin. - - `rank`: SEO rank. - - `is_news_domain`: Boolean indicating if the source is a news domain. - - `news_domain_type`: Type of news domain (e.g., "Original Content"). - - `news_type`: Category of news (e.g., "General News Outlets"). - - is_news_domain : typing.Optional[bool] - If true, filters results to include only news domains. - - news_domain_type : typing.Optional[SourcesGetRequestNewsDomainType] - Filters results based on the news domain type. Possible values are: - - `Original Content`: Sources that produce their own content. - - `Aggregator`: Sources that collect content from various other sources. - - `Press Releases`: Sources primarily publishing press releases. - - `Republisher`: Sources that republish content from other sources. - - `Other`: Sources that don't fit into main categories. + include_additional_info : typing.Optional[IncludeAdditionalInfo] + + is_news_domain : typing.Optional[IsNewsDomain] + + news_domain_type : typing.Optional[NewsDomainType] news_type : typing.Optional[str] Filters results based on the news type. Multiple types can be specified using a comma-separated string. - Example: `"General News Outlets,Tech News and Updates"` + For a complete list of available news types, see [Enumerated parameters > News type](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#news-type-news-type). - For a complete list of available news types, see [Enumerated parameters > News type](/docs/v3/api-reference/overview/enumerated-parameters#news-type-news-type). - - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -377,8 +324,16 @@ async def get( async def main() -> None: await client.sources.get( - predefined_sources="top 100 US, top 5 GB", + lang="en,es", + countries="US,CA", + predefined_sources="top 50 US, top 20 GB", + source_name="sport,tech", source_url="bbc.com", + include_additional_info=True, + is_news_domain=True, + news_type="General News Outlets,Tech News and Updates", + from_rank=100, + to_rank=100, ) @@ -464,11 +419,7 @@ async def post( async def main() -> None: await client.sources.post( - predefined_sources=["top 50 US"], - include_additional_info=True, - is_news_domain=True, - news_domain_type="Original Content", - news_type="General News Outlets", + predefined_sources="top 10 US", ) diff --git a/src/newscatcher/sources/raw_client.py b/src/newscatcher/sources/raw_client.py index afb6c3b..bee2751 100644 --- a/src/newscatcher/sources/raw_client.py +++ b/src/newscatcher/sources/raw_client.py @@ -6,6 +6,7 @@ from ..core.api_error import ApiError from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.parse_error import ParsingError from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..core.serialization import convert_and_respect_annotation_metadata @@ -29,7 +30,7 @@ from ..types.source_url import SourceUrl from ..types.sources_response_dto import SourcesResponseDto from ..types.to_rank import ToRank -from .types.sources_get_request_news_domain_type import SourcesGetRequestNewsDomainType +from pydantic import ValidationError # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -47,12 +48,12 @@ def get( predefined_sources: typing.Optional[str] = None, source_name: typing.Optional[str] = None, source_url: typing.Optional[str] = None, - include_additional_info: typing.Optional[bool] = None, - is_news_domain: typing.Optional[bool] = None, - news_domain_type: typing.Optional[SourcesGetRequestNewsDomainType] = None, + include_additional_info: typing.Optional[IncludeAdditionalInfo] = None, + is_news_domain: typing.Optional[IsNewsDomain] = None, + news_domain_type: typing.Optional[NewsDomainType] = None, news_type: typing.Optional[str] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[SourcesResponseDto]: """ @@ -63,33 +64,23 @@ def get( lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. source_name : typing.Optional[str] Word or phrase to search within the source names. To specify multiple values, use a comma-separated string. - Example: `"sport, tech"` - **Note**: The search doesn't require an exact match and returns sources containing the specified terms in their names. You can use any word or phrase, like `"sport"` or `"new york times"`. For example, `"sport"` returns sources such as `"Motorsport"`, `"Dot Esport"`, and `"Tuttosport"`. source_url : typing.Optional[str] @@ -98,38 +89,20 @@ def get( **Caution**: When specifying the `source_url` parameter, you can only use `include_additional_info` as an extra parameter. - include_additional_info : typing.Optional[bool] - If true, returns the following additional datapoints about each news source: - - `nb_articles_for_7d`: The number of articles published by the source in the last week. - - `country`: Source country of origin. - - `rank`: SEO rank. - - `is_news_domain`: Boolean indicating if the source is a news domain. - - `news_domain_type`: Type of news domain (e.g., "Original Content"). - - `news_type`: Category of news (e.g., "General News Outlets"). - - is_news_domain : typing.Optional[bool] - If true, filters results to include only news domains. - - news_domain_type : typing.Optional[SourcesGetRequestNewsDomainType] - Filters results based on the news domain type. Possible values are: - - `Original Content`: Sources that produce their own content. - - `Aggregator`: Sources that collect content from various other sources. - - `Press Releases`: Sources primarily publishing press releases. - - `Republisher`: Sources that republish content from other sources. - - `Other`: Sources that don't fit into main categories. + include_additional_info : typing.Optional[IncludeAdditionalInfo] + + is_news_domain : typing.Optional[IsNewsDomain] + + news_domain_type : typing.Optional[NewsDomainType] news_type : typing.Optional[str] Filters results based on the news type. Multiple types can be specified using a comma-separated string. - Example: `"General News Outlets,Tech News and Updates"` - - For a complete list of available news types, see [Enumerated parameters > News type](/docs/v3/api-reference/overview/enumerated-parameters#news-type-news-type). + For a complete list of available news types, see [Enumerated parameters > News type](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#news-type-news-type). - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -247,6 +220,10 @@ def get( _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 post( @@ -422,6 +399,10 @@ def post( _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) @@ -437,12 +418,12 @@ async def get( predefined_sources: typing.Optional[str] = None, source_name: typing.Optional[str] = None, source_url: typing.Optional[str] = None, - include_additional_info: typing.Optional[bool] = None, - is_news_domain: typing.Optional[bool] = None, - news_domain_type: typing.Optional[SourcesGetRequestNewsDomainType] = None, + include_additional_info: typing.Optional[IncludeAdditionalInfo] = None, + is_news_domain: typing.Optional[IsNewsDomain] = None, + news_domain_type: typing.Optional[NewsDomainType] = None, news_type: typing.Optional[str] = None, - from_rank: typing.Optional[int] = None, - to_rank: typing.Optional[int] = None, + from_rank: typing.Optional[FromRank] = None, + to_rank: typing.Optional[ToRank] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[SourcesResponseDto]: """ @@ -453,33 +434,23 @@ async def get( lang : typing.Optional[str] The language(s) of the search. The only accepted format is the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) code. To select multiple languages, use a comma-separated string. - Example: `"en, es"` - - To learn more, see [Enumerated parameters > Language](/docs/v3/api-reference/overview/enumerated-parameters#language-lang-and-not-lang). + To learn more, see [Enumerated parameters > Language](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#language-lang-and-not-lang). countries : typing.Optional[str] The countries where the news publisher is located. The accepted format is the two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code. To select multiple countries, use a comma-separated string. - Example: `"US, CA"` - - To learn more, see [Enumerated parameters > Country](/docs/v3/api-reference/overview/enumerated-parameters#country-country-and-not-country). + To learn more, see [Enumerated parameters > Country](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#country-country-and-not-country). predefined_sources : typing.Optional[str] Predefined top news sources per country. - Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Multiple countries with the number of top sources can be specified as a comma-separated string. + Format: start with the word `top`, followed by the number of desired sources, and then the two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). - Examples: - - `"top 100 US"` - - `"top 33 AT"` - - `"top 50 US, top 20 GB"` - - `"top 33 AT, top 50 IT"` + Multiple countries with the number of top sources can be specified as a comma-separated string. source_name : typing.Optional[str] Word or phrase to search within the source names. To specify multiple values, use a comma-separated string. - Example: `"sport, tech"` - **Note**: The search doesn't require an exact match and returns sources containing the specified terms in their names. You can use any word or phrase, like `"sport"` or `"new york times"`. For example, `"sport"` returns sources such as `"Motorsport"`, `"Dot Esport"`, and `"Tuttosport"`. source_url : typing.Optional[str] @@ -488,38 +459,20 @@ async def get( **Caution**: When specifying the `source_url` parameter, you can only use `include_additional_info` as an extra parameter. - include_additional_info : typing.Optional[bool] - If true, returns the following additional datapoints about each news source: - - `nb_articles_for_7d`: The number of articles published by the source in the last week. - - `country`: Source country of origin. - - `rank`: SEO rank. - - `is_news_domain`: Boolean indicating if the source is a news domain. - - `news_domain_type`: Type of news domain (e.g., "Original Content"). - - `news_type`: Category of news (e.g., "General News Outlets"). - - is_news_domain : typing.Optional[bool] - If true, filters results to include only news domains. - - news_domain_type : typing.Optional[SourcesGetRequestNewsDomainType] - Filters results based on the news domain type. Possible values are: - - `Original Content`: Sources that produce their own content. - - `Aggregator`: Sources that collect content from various other sources. - - `Press Releases`: Sources primarily publishing press releases. - - `Republisher`: Sources that republish content from other sources. - - `Other`: Sources that don't fit into main categories. + include_additional_info : typing.Optional[IncludeAdditionalInfo] + + is_news_domain : typing.Optional[IsNewsDomain] + + news_domain_type : typing.Optional[NewsDomainType] news_type : typing.Optional[str] Filters results based on the news type. Multiple types can be specified using a comma-separated string. - Example: `"General News Outlets,Tech News and Updates"` - - For a complete list of available news types, see [Enumerated parameters > News type](/docs/v3/api-reference/overview/enumerated-parameters#news-type-news-type). + For a complete list of available news types, see [Enumerated parameters > News type](https://www.newscatcherapi.com/docs/news-api/api-reference/enumerated-parameters#news-type-news-type). - from_rank : typing.Optional[int] - The lowest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + from_rank : typing.Optional[FromRank] - to_rank : typing.Optional[int] - The highest boundary of the rank of a news website to filter by. A lower rank indicates a more popular source. + to_rank : typing.Optional[ToRank] request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -637,6 +590,10 @@ async def get( _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 post( @@ -812,4 +769,8 @@ async def post( _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) diff --git a/src/newscatcher/sources/types/__init__.py b/src/newscatcher/sources/types/__init__.py deleted file mode 100644 index d40ef61..0000000 --- a/src/newscatcher/sources/types/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -from .sources_get_request_news_domain_type import SourcesGetRequestNewsDomainType - -__all__ = ["SourcesGetRequestNewsDomainType"] diff --git a/src/newscatcher/sources/types/sources_get_request_news_domain_type.py b/src/newscatcher/sources/types/sources_get_request_news_domain_type.py deleted file mode 100644 index f3eee76..0000000 --- a/src/newscatcher/sources/types/sources_get_request_news_domain_type.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -SourcesGetRequestNewsDomainType = typing.Union[ - typing.Literal["Original Content", "Aggregator", "Press Releases", "Republisher", "Other"], typing.Any -] diff --git a/src/newscatcher/subscription/raw_client.py b/src/newscatcher/subscription/raw_client.py index 3aa11f5..6bcbfed 100644 --- a/src/newscatcher/subscription/raw_client.py +++ b/src/newscatcher/subscription/raw_client.py @@ -6,6 +6,7 @@ from ..core.api_error import ApiError from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.parse_error import ParsingError from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..errors.bad_request_error import BadRequestError @@ -17,6 +18,7 @@ from ..errors.unprocessable_entity_error import UnprocessableEntityError from ..types.error import Error from ..types.subscription_response_dto import SubscriptionResponseDto +from pydantic import ValidationError class RawSubscriptionClient: @@ -132,6 +134,10 @@ def get(self, *, request_options: typing.Optional[RequestOptions] = None) -> Htt _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 post(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[SubscriptionResponseDto]: @@ -243,6 +249,10 @@ def post(self, *, request_options: typing.Optional[RequestOptions] = None) -> Ht _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) @@ -361,6 +371,10 @@ async def get( _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 post( @@ -474,4 +488,8 @@ async def post( _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) diff --git a/src/newscatcher/types/__init__.py b/src/newscatcher/types/__init__.py index b458a49..45d2cb7 100644 --- a/src/newscatcher/types/__init__.py +++ b/src/newscatcher/types/__init__.py @@ -2,112 +2,240 @@ # isort: skip_file -from .additional_domain_info import AdditionalDomainInfo -from .additional_domain_info_entity import AdditionalDomainInfoEntity -from .additional_source_info import AdditionalSourceInfo -from .aggregation_by import AggregationBy -from .aggregation_count_response_dto import AggregationCountResponseDto -from .aggregation_item import AggregationItem -from .aggregations import Aggregations -from .all_domain_links import AllDomainLinks -from .all_links import AllLinks -from .article_entity import ArticleEntity -from .article_entity_all_domain_links import ArticleEntityAllDomainLinks -from .article_entity_all_links import ArticleEntityAllLinks -from .articles import Articles -from .author_name import AuthorName -from .authors import Authors -from .base_search_response_dto import BaseSearchResponseDto -from .breaking_news_article_entity import BreakingNewsArticleEntity -from .breaking_news_article_entity_all_domain_links import BreakingNewsArticleEntityAllDomainLinks -from .breaking_news_article_entity_all_links import BreakingNewsArticleEntityAllLinks -from .breaking_news_event_entity import BreakingNewsEventEntity -from .breaking_news_response_dto import BreakingNewsResponseDto -from .by_parse_date import ByParseDate -from .cluster_entity import ClusterEntity -from .clustered_articles_dto import ClusteredArticlesDto -from .clustered_search_response_dto import ClusteredSearchResponseDto -from .clustering_enabled import ClusteringEnabled -from .clustering_threshold import ClusteringThreshold -from .clustering_variable import ClusteringVariable -from .content_sentiment_max import ContentSentimentMax -from .content_sentiment_min import ContentSentimentMin -from .countries import Countries -from .custom_tags import CustomTags -from .error import Error -from .exclude_duplicates import ExcludeDuplicates -from .failed_aggregation_count_response_dto import FailedAggregationCountResponseDto -from .failed_authors_response_dto import FailedAuthorsResponseDto -from .failed_search_similar_response_dto import FailedSearchSimilarResponseDto -from .from_ import From -from .from_rank import FromRank -from .has_nlp import HasNlp -from .iab_tags import IabTags -from .ids import Ids -from .include_additional_info import IncludeAdditionalInfo -from .include_nlp_data import IncludeNlpData -from .include_similar_documents import IncludeSimilarDocuments -from .include_translation_fields import IncludeTranslationFields -from .iptc_tags import IptcTags -from .is_headline import IsHeadline -from .is_news_domain import IsNewsDomain -from .is_opinion import IsOpinion -from .is_paid_content import IsPaidContent -from .journalists import Journalists -from .lang import Lang -from .links import Links -from .loc_entity_name import LocEntityName -from .misc_entity_name import MiscEntityName -from .named_entity_list import NamedEntityList -from .named_entity_list_item import NamedEntityListItem -from .ner_name import NerName -from .news_domain_type import NewsDomainType -from .news_type import NewsType -from .nlp_data_entity import NlpDataEntity -from .not_author_name import NotAuthorName -from .not_countries import NotCountries -from .not_iab_tags import NotIabTags -from .not_iptc_tags import NotIptcTags -from .not_lang import NotLang -from .not_sources import NotSources -from .not_theme import NotTheme -from .org_entity_name import OrgEntityName -from .page import Page -from .page_size import PageSize -from .parent_url import ParentUrl -from .per_entity_name import PerEntityName -from .predefined_sources import PredefinedSources -from .published_date_precision import PublishedDatePrecision -from .q import Q -from .ranked_only import RankedOnly -from .robots_compliant import RobotsCompliant -from .search_in import SearchIn -from .search_response_dto import SearchResponseDto -from .search_similar_response_dto import SearchSimilarResponseDto -from .sentiment_scores import SentimentScores -from .similar_article_entity import SimilarArticleEntity -from .similar_document import SimilarDocument -from .similar_documents_fields import SimilarDocumentsFields -from .similar_documents_number import SimilarDocumentsNumber -from .sort_by import SortBy -from .source_info import SourceInfo -from .source_name import SourceName -from .source_url import SourceUrl -from .sources import Sources -from .sources_response_dto import SourcesResponseDto -from .sources_response_dto_sources_item import SourcesResponseDtoSourcesItem -from .subscription_response_dto import SubscriptionResponseDto -from .theme import Theme -from .time_frame_count import TimeFrameCount -from .title_sentiment_max import TitleSentimentMax -from .title_sentiment_min import TitleSentimentMin -from .to import To -from .to_rank import ToRank -from .top_n_articles import TopNArticles -from .user_input_dto import UserInputDto -from .when import When -from .word_count_max import WordCountMax -from .word_count_min import WordCountMin +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .additional_domain_info import AdditionalDomainInfo + from .additional_domain_info_entity import AdditionalDomainInfoEntity + from .additional_source_info import AdditionalSourceInfo + from .aggregation_by import AggregationBy + from .aggregation_count_response_dto import AggregationCountResponseDto + from .aggregation_count_response_dto_aggregations import AggregationCountResponseDtoAggregations + from .aggregation_item import AggregationItem + from .all_domain_links import AllDomainLinks + from .all_links import AllLinks + from .all_links_data_item import AllLinksDataItem + from .all_links_text import AllLinksText + from .article_entity import ArticleEntity + from .article_entity_all_domain_links import ArticleEntityAllDomainLinks + from .article_entity_all_links import ArticleEntityAllLinks + from .article_entity_authors import ArticleEntityAuthors + from .article_entity_journalists import ArticleEntityJournalists + from .articles_dto import ArticlesDto + from .author_name import AuthorName + from .base_search_response_dto import BaseSearchResponseDto + from .breaking_news_article_entity import BreakingNewsArticleEntity + from .breaking_news_article_entity_all_domain_links import BreakingNewsArticleEntityAllDomainLinks + from .breaking_news_article_entity_all_links import BreakingNewsArticleEntityAllLinks + from .breaking_news_article_entity_authors import BreakingNewsArticleEntityAuthors + from .breaking_news_article_entity_journalists import BreakingNewsArticleEntityJournalists + from .breaking_news_event_entity import BreakingNewsEventEntity + from .breaking_news_response_dto import BreakingNewsResponseDto + from .by_parse_date import ByParseDate + from .cluster_entity import ClusterEntity + from .clustered_articles_dto import ClusteredArticlesDto + from .clustered_search_response_dto import ClusteredSearchResponseDto + from .clustering_enabled import ClusteringEnabled + from .clustering_threshold import ClusteringThreshold + from .clustering_variable import ClusteringVariable + from .content_sentiment_max import ContentSentimentMax + from .content_sentiment_min import ContentSentimentMin + from .countries import Countries + from .custom_tags import CustomTags + from .error import Error + from .exclude_duplicates import ExcludeDuplicates + from .failed_aggregation_count_response_dto import FailedAggregationCountResponseDto + from .failed_authors_response_dto import FailedAuthorsResponseDto + from .from_ import From + from .from_rank import FromRank + from .has_nlp import HasNlp + from .iab_tags import IabTags + from .ids import Ids + from .include_additional_info import IncludeAdditionalInfo + from .include_nlp_data import IncludeNlpData + from .include_translation_fields import IncludeTranslationFields + from .iptc_tags import IptcTags + from .is_headline import IsHeadline + from .is_news_domain import IsNewsDomain + from .is_opinion import IsOpinion + from .is_paid_content import IsPaidContent + from .lang import Lang + from .links import Links + from .loc_entity_name import LocEntityName + from .misc_entity_name import MiscEntityName + from .named_entity_list import NamedEntityList + from .named_entity_list_item import NamedEntityListItem + from .ner_name import NerName + from .news_domain_type import NewsDomainType + from .news_type import NewsType + from .nlp_data_entity import NlpDataEntity + from .not_author_name import NotAuthorName + from .not_countries import NotCountries + from .not_iab_tags import NotIabTags + from .not_iptc_tags import NotIptcTags + from .not_lang import NotLang + from .not_sources import NotSources + from .not_theme import NotTheme + from .org_entity_name import OrgEntityName + from .page import Page + from .page_size import PageSize + from .parent_url import ParentUrl + from .per_entity_name import PerEntityName + from .predefined_sources import PredefinedSources + from .published_date_precision import PublishedDatePrecision + from .q import Q + from .ranked_only import RankedOnly + from .robots_compliant import RobotsCompliant + from .search_in import SearchIn + from .search_response_dto import SearchResponseDto + from .sentiment_scores import SentimentScores + from .sort_by import SortBy + from .source_info import SourceInfo + from .source_name import SourceName + from .source_url import SourceUrl + from .sources import Sources + from .sources_response_dto import SourcesResponseDto + from .sources_response_dto_sources_item import SourcesResponseDtoSourcesItem + from .subscription_response_dto import SubscriptionResponseDto + from .theme import Theme + from .time_frame_count import TimeFrameCount + from .title_sentiment_max import TitleSentimentMax + from .title_sentiment_min import TitleSentimentMin + from .to import To + from .to_rank import ToRank + from .top_n_articles import TopNArticles + from .user_input_dto import UserInputDto + from .when import When + from .word_count_max import WordCountMax + from .word_count_min import WordCountMin +_dynamic_imports: typing.Dict[str, str] = { + "AdditionalDomainInfo": ".additional_domain_info", + "AdditionalDomainInfoEntity": ".additional_domain_info_entity", + "AdditionalSourceInfo": ".additional_source_info", + "AggregationBy": ".aggregation_by", + "AggregationCountResponseDto": ".aggregation_count_response_dto", + "AggregationCountResponseDtoAggregations": ".aggregation_count_response_dto_aggregations", + "AggregationItem": ".aggregation_item", + "AllDomainLinks": ".all_domain_links", + "AllLinks": ".all_links", + "AllLinksDataItem": ".all_links_data_item", + "AllLinksText": ".all_links_text", + "ArticleEntity": ".article_entity", + "ArticleEntityAllDomainLinks": ".article_entity_all_domain_links", + "ArticleEntityAllLinks": ".article_entity_all_links", + "ArticleEntityAuthors": ".article_entity_authors", + "ArticleEntityJournalists": ".article_entity_journalists", + "ArticlesDto": ".articles_dto", + "AuthorName": ".author_name", + "BaseSearchResponseDto": ".base_search_response_dto", + "BreakingNewsArticleEntity": ".breaking_news_article_entity", + "BreakingNewsArticleEntityAllDomainLinks": ".breaking_news_article_entity_all_domain_links", + "BreakingNewsArticleEntityAllLinks": ".breaking_news_article_entity_all_links", + "BreakingNewsArticleEntityAuthors": ".breaking_news_article_entity_authors", + "BreakingNewsArticleEntityJournalists": ".breaking_news_article_entity_journalists", + "BreakingNewsEventEntity": ".breaking_news_event_entity", + "BreakingNewsResponseDto": ".breaking_news_response_dto", + "ByParseDate": ".by_parse_date", + "ClusterEntity": ".cluster_entity", + "ClusteredArticlesDto": ".clustered_articles_dto", + "ClusteredSearchResponseDto": ".clustered_search_response_dto", + "ClusteringEnabled": ".clustering_enabled", + "ClusteringThreshold": ".clustering_threshold", + "ClusteringVariable": ".clustering_variable", + "ContentSentimentMax": ".content_sentiment_max", + "ContentSentimentMin": ".content_sentiment_min", + "Countries": ".countries", + "CustomTags": ".custom_tags", + "Error": ".error", + "ExcludeDuplicates": ".exclude_duplicates", + "FailedAggregationCountResponseDto": ".failed_aggregation_count_response_dto", + "FailedAuthorsResponseDto": ".failed_authors_response_dto", + "From": ".from_", + "FromRank": ".from_rank", + "HasNlp": ".has_nlp", + "IabTags": ".iab_tags", + "Ids": ".ids", + "IncludeAdditionalInfo": ".include_additional_info", + "IncludeNlpData": ".include_nlp_data", + "IncludeTranslationFields": ".include_translation_fields", + "IptcTags": ".iptc_tags", + "IsHeadline": ".is_headline", + "IsNewsDomain": ".is_news_domain", + "IsOpinion": ".is_opinion", + "IsPaidContent": ".is_paid_content", + "Lang": ".lang", + "Links": ".links", + "LocEntityName": ".loc_entity_name", + "MiscEntityName": ".misc_entity_name", + "NamedEntityList": ".named_entity_list", + "NamedEntityListItem": ".named_entity_list_item", + "NerName": ".ner_name", + "NewsDomainType": ".news_domain_type", + "NewsType": ".news_type", + "NlpDataEntity": ".nlp_data_entity", + "NotAuthorName": ".not_author_name", + "NotCountries": ".not_countries", + "NotIabTags": ".not_iab_tags", + "NotIptcTags": ".not_iptc_tags", + "NotLang": ".not_lang", + "NotSources": ".not_sources", + "NotTheme": ".not_theme", + "OrgEntityName": ".org_entity_name", + "Page": ".page", + "PageSize": ".page_size", + "ParentUrl": ".parent_url", + "PerEntityName": ".per_entity_name", + "PredefinedSources": ".predefined_sources", + "PublishedDatePrecision": ".published_date_precision", + "Q": ".q", + "RankedOnly": ".ranked_only", + "RobotsCompliant": ".robots_compliant", + "SearchIn": ".search_in", + "SearchResponseDto": ".search_response_dto", + "SentimentScores": ".sentiment_scores", + "SortBy": ".sort_by", + "SourceInfo": ".source_info", + "SourceName": ".source_name", + "SourceUrl": ".source_url", + "Sources": ".sources", + "SourcesResponseDto": ".sources_response_dto", + "SourcesResponseDtoSourcesItem": ".sources_response_dto_sources_item", + "SubscriptionResponseDto": ".subscription_response_dto", + "Theme": ".theme", + "TimeFrameCount": ".time_frame_count", + "TitleSentimentMax": ".title_sentiment_max", + "TitleSentimentMin": ".title_sentiment_min", + "To": ".to", + "ToRank": ".to_rank", + "TopNArticles": ".top_n_articles", + "UserInputDto": ".user_input_dto", + "When": ".when", + "WordCountMax": ".word_count_max", + "WordCountMin": ".word_count_min", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + __all__ = [ "AdditionalDomainInfo", @@ -115,20 +243,25 @@ "AdditionalSourceInfo", "AggregationBy", "AggregationCountResponseDto", + "AggregationCountResponseDtoAggregations", "AggregationItem", - "Aggregations", "AllDomainLinks", "AllLinks", + "AllLinksDataItem", + "AllLinksText", "ArticleEntity", "ArticleEntityAllDomainLinks", "ArticleEntityAllLinks", - "Articles", + "ArticleEntityAuthors", + "ArticleEntityJournalists", + "ArticlesDto", "AuthorName", - "Authors", "BaseSearchResponseDto", "BreakingNewsArticleEntity", "BreakingNewsArticleEntityAllDomainLinks", "BreakingNewsArticleEntityAllLinks", + "BreakingNewsArticleEntityAuthors", + "BreakingNewsArticleEntityJournalists", "BreakingNewsEventEntity", "BreakingNewsResponseDto", "ByParseDate", @@ -146,7 +279,6 @@ "ExcludeDuplicates", "FailedAggregationCountResponseDto", "FailedAuthorsResponseDto", - "FailedSearchSimilarResponseDto", "From", "FromRank", "HasNlp", @@ -154,14 +286,12 @@ "Ids", "IncludeAdditionalInfo", "IncludeNlpData", - "IncludeSimilarDocuments", "IncludeTranslationFields", "IptcTags", "IsHeadline", "IsNewsDomain", "IsOpinion", "IsPaidContent", - "Journalists", "Lang", "Links", "LocEntityName", @@ -191,12 +321,7 @@ "RobotsCompliant", "SearchIn", "SearchResponseDto", - "SearchSimilarResponseDto", "SentimentScores", - "SimilarArticleEntity", - "SimilarDocument", - "SimilarDocumentsFields", - "SimilarDocumentsNumber", "SortBy", "SourceInfo", "SourceName", diff --git a/src/newscatcher/types/additional_source_info.py b/src/newscatcher/types/additional_source_info.py index c9551f1..55f984f 100644 --- a/src/newscatcher/types/additional_source_info.py +++ b/src/newscatcher/types/additional_source_info.py @@ -14,12 +14,13 @@ class AdditionalSourceInfo(UniversalBaseModel): """ nb_articles_for_7_d: typing_extensions.Annotated[ - typing.Optional[int], FieldMetadata(alias="nb_articles_for_7d") - ] = pydantic.Field(default=None) - """ - The number of articles published by the source in the last seven days. - """ - + typing.Optional[int], + FieldMetadata(alias="nb_articles_for_7d"), + pydantic.Field( + alias="nb_articles_for_7d", + description="The number of articles published by the source in the last seven days.", + ), + ] = None country: typing.Optional[str] = pydantic.Field(default=None) """ The country of origin of the news source. @@ -45,6 +46,11 @@ class AdditionalSourceInfo(UniversalBaseModel): The category of news provided by the source. """ + robots_compliant: typing.Optional[str] = pydantic.Field(default=None) + """ + Percentage of domain articles that comply with robots.txt scraping rules (0-100%). + """ + if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/newscatcher/types/aggregation_count_response_dto.py b/src/newscatcher/types/aggregation_count_response_dto.py index 8492c80..3b3e96a 100644 --- a/src/newscatcher/types/aggregation_count_response_dto.py +++ b/src/newscatcher/types/aggregation_count_response_dto.py @@ -4,7 +4,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2 -from .aggregations import Aggregations +from .aggregation_count_response_dto_aggregations import AggregationCountResponseDtoAggregations from .base_search_response_dto import BaseSearchResponseDto from .user_input_dto import UserInputDto @@ -16,7 +16,7 @@ class AggregationCountResponseDto(BaseSearchResponseDto): - Optional fields may be `null` or `undefined` if the data point is not presented or couldn't be extracted during processing. """ - aggregations: typing.Optional[Aggregations] = pydantic.Field(default=None) + aggregations: typing.Optional[AggregationCountResponseDtoAggregations] = pydantic.Field(default=None) """ The aggregation results. Can be either a dictionary or a list of dictionaries. """ diff --git a/src/newscatcher/types/aggregations.py b/src/newscatcher/types/aggregation_count_response_dto_aggregations.py similarity index 55% rename from src/newscatcher/types/aggregations.py rename to src/newscatcher/types/aggregation_count_response_dto_aggregations.py index 16b1d5f..aa0eb09 100644 --- a/src/newscatcher/types/aggregations.py +++ b/src/newscatcher/types/aggregation_count_response_dto_aggregations.py @@ -4,4 +4,4 @@ from .aggregation_item import AggregationItem -Aggregations = typing.Union[AggregationItem, typing.List[AggregationItem]] +AggregationCountResponseDtoAggregations = typing.Union[AggregationItem, typing.List[AggregationItem]] diff --git a/src/newscatcher/types/similar_document.py b/src/newscatcher/types/all_links_data_item.py similarity index 57% rename from src/newscatcher/types/similar_document.py rename to src/newscatcher/types/all_links_data_item.py index 4ffc9ce..8bfdff1 100644 --- a/src/newscatcher/types/similar_document.py +++ b/src/newscatcher/types/all_links_data_item.py @@ -6,29 +6,24 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -class SimilarDocument(UniversalBaseModel): +class AllLinksDataItem(UniversalBaseModel): """ - The data model for a similar document in the `Search similar` articles request. + Detailed information about a link found in an article. """ - id: str = pydantic.Field() + domain_url: str = pydantic.Field() """ - The unique identifier of the similar document. + The domain of the linked URL. """ - score: float = pydantic.Field() - """ - The relevance score of the similar document. - """ - - title: str = pydantic.Field() + link: str = pydantic.Field() """ - The title of the similar document. + The complete URL of the link. """ - link: str = pydantic.Field() + text: str = pydantic.Field() """ - The link to the similar document. + The anchor text of the link. """ if IS_PYDANTIC_V2: diff --git a/src/newscatcher/types/authors.py b/src/newscatcher/types/all_links_text.py similarity index 61% rename from src/newscatcher/types/authors.py rename to src/newscatcher/types/all_links_text.py index 2393876..f1cf578 100644 --- a/src/newscatcher/types/authors.py +++ b/src/newscatcher/types/all_links_text.py @@ -2,4 +2,4 @@ import typing -Authors = typing.Union[typing.List[str], str] +AllLinksText = typing.Union[str, typing.List[str]] diff --git a/src/newscatcher/types/article_entity.py b/src/newscatcher/types/article_entity.py index ff89b24..676f58a 100644 --- a/src/newscatcher/types/article_entity.py +++ b/src/newscatcher/types/article_entity.py @@ -5,10 +5,11 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from .additional_domain_info_entity import AdditionalDomainInfoEntity +from .all_links_data_item import AllLinksDataItem from .article_entity_all_domain_links import ArticleEntityAllDomainLinks from .article_entity_all_links import ArticleEntityAllLinks -from .authors import Authors -from .journalists import Journalists +from .article_entity_authors import ArticleEntityAuthors +from .article_entity_journalists import ArticleEntityJournalists from .nlp_data_entity import NlpDataEntity @@ -27,12 +28,12 @@ class ArticleEntity(UniversalBaseModel): The primary author of the article. """ - authors: typing.Optional[Authors] = pydantic.Field(default=None) + authors: typing.Optional[ArticleEntityAuthors] = pydantic.Field(default=None) """ A list of authors of the article. """ - journalists: typing.Optional[Journalists] = pydantic.Field(default=None) + journalists: typing.Optional[ArticleEntityJournalists] = pydantic.Field(default=None) """ A list of journalists associated with the article. """ @@ -89,7 +90,7 @@ class ArticleEntity(UniversalBaseModel): paid_content: typing.Optional[bool] = pydantic.Field(default=None) """ - Indicates if the article is paid content. + Indicates whether the source labels the article as paywalled or requiring a subscription for full access. """ parent_url: str = pydantic.Field() @@ -127,7 +128,7 @@ class ArticleEntity(UniversalBaseModel): A brief description of the article. """ - content: str = pydantic.Field() + content: typing.Optional[str] = pydantic.Field(default=None) """ The content of the article. """ @@ -167,6 +168,11 @@ class ArticleEntity(UniversalBaseModel): A list of all domain URLs mentioned in the article. """ + all_links_data: typing.Optional[typing.List[AllLinksDataItem]] = pydantic.Field(default=None) + """ + Detailed information about all links mentioned in the article, including link URL, domain, and anchor text. Only present when the `all_links_text` parameter is used in the request. + """ + nlp: typing.Optional[NlpDataEntity] = None id: str = pydantic.Field() """ diff --git a/src/newscatcher/types/journalists.py b/src/newscatcher/types/article_entity_authors.py similarity index 57% rename from src/newscatcher/types/journalists.py rename to src/newscatcher/types/article_entity_authors.py index 5dfe0f6..f1dd11f 100644 --- a/src/newscatcher/types/journalists.py +++ b/src/newscatcher/types/article_entity_authors.py @@ -2,4 +2,4 @@ import typing -Journalists = typing.Union[typing.List[str], str] +ArticleEntityAuthors = typing.Union[typing.List[str], str] diff --git a/src/newscatcher/types/article_entity_journalists.py b/src/newscatcher/types/article_entity_journalists.py new file mode 100644 index 0000000..c9dabe1 --- /dev/null +++ b/src/newscatcher/types/article_entity_journalists.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ArticleEntityJournalists = typing.Union[typing.List[str], str] diff --git a/src/newscatcher/types/articles.py b/src/newscatcher/types/articles_dto.py similarity index 75% rename from src/newscatcher/types/articles.py rename to src/newscatcher/types/articles_dto.py index 14d2ac6..839bb31 100644 --- a/src/newscatcher/types/articles.py +++ b/src/newscatcher/types/articles_dto.py @@ -4,4 +4,4 @@ from .article_entity import ArticleEntity -Articles = typing.List[ArticleEntity] +ArticlesDto = typing.List[ArticleEntity] diff --git a/src/newscatcher/types/breaking_news_article_entity.py b/src/newscatcher/types/breaking_news_article_entity.py index 0a76272..0f06403 100644 --- a/src/newscatcher/types/breaking_news_article_entity.py +++ b/src/newscatcher/types/breaking_news_article_entity.py @@ -4,10 +4,10 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .authors import Authors from .breaking_news_article_entity_all_domain_links import BreakingNewsArticleEntityAllDomainLinks from .breaking_news_article_entity_all_links import BreakingNewsArticleEntityAllLinks -from .journalists import Journalists +from .breaking_news_article_entity_authors import BreakingNewsArticleEntityAuthors +from .breaking_news_article_entity_journalists import BreakingNewsArticleEntityJournalists from .nlp_data_entity import NlpDataEntity @@ -26,12 +26,12 @@ class BreakingNewsArticleEntity(UniversalBaseModel): The primary author of the article. """ - authors: typing.Optional[Authors] = pydantic.Field(default=None) + authors: typing.Optional[BreakingNewsArticleEntityAuthors] = pydantic.Field(default=None) """ A list of authors of the article. """ - journalists: typing.Optional[Journalists] = pydantic.Field(default=None) + journalists: typing.Optional[BreakingNewsArticleEntityJournalists] = pydantic.Field(default=None) """ A list of journalists associated with the article. """ @@ -88,7 +88,7 @@ class BreakingNewsArticleEntity(UniversalBaseModel): paid_content: typing.Optional[bool] = pydantic.Field(default=None) """ - Indicates if the article is paid content. + Indicates whether the source labels the article as paywalled or requiring a subscription for full access. """ parent_url: str = pydantic.Field() @@ -126,11 +126,21 @@ class BreakingNewsArticleEntity(UniversalBaseModel): A brief description of the article. """ - content: str = pydantic.Field() + content: typing.Optional[str] = pydantic.Field(default=None) """ The content of the article. """ + title_translated_en: typing.Optional[str] = pydantic.Field(default=None) + """ + English translation of the article title. Available when setting the `include_translation_fields` parameter to `true`. + """ + + content_translated_en: typing.Optional[str] = pydantic.Field(default=None) + """ + English translation of the article content. Available when setting the `include_translation_fields` parameter to `true`. + """ + word_count: typing.Optional[int] = pydantic.Field(default=None) """ The word count of the article. @@ -167,11 +177,6 @@ class BreakingNewsArticleEntity(UniversalBaseModel): The relevance score of the article. """ - robots_compliant: typing.Optional[bool] = pydantic.Field(default=None) - """ - True if the article content can be safely accessed according to the publisher's robots.txt rules; false otherwise. - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/newscatcher/types/breaking_news_article_entity_authors.py b/src/newscatcher/types/breaking_news_article_entity_authors.py new file mode 100644 index 0000000..63be474 --- /dev/null +++ b/src/newscatcher/types/breaking_news_article_entity_authors.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +BreakingNewsArticleEntityAuthors = typing.Union[typing.List[str], str] diff --git a/src/newscatcher/types/breaking_news_article_entity_journalists.py b/src/newscatcher/types/breaking_news_article_entity_journalists.py new file mode 100644 index 0000000..e541fff --- /dev/null +++ b/src/newscatcher/types/breaking_news_article_entity_journalists.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +BreakingNewsArticleEntityJournalists = typing.Union[typing.List[str], str] diff --git a/src/newscatcher/types/failed_authors_response_dto.py b/src/newscatcher/types/failed_authors_response_dto.py index 7345a65..43f4181 100644 --- a/src/newscatcher/types/failed_authors_response_dto.py +++ b/src/newscatcher/types/failed_authors_response_dto.py @@ -13,7 +13,7 @@ class FailedAuthorsResponseDto(BaseSearchResponseDto): The response model for a failed `Authors` search request. """ - articles: typing.Optional[typing.List[typing.Dict[str, typing.Optional[typing.Any]]]] = pydantic.Field(default=None) + articles: typing.Optional[typing.List[typing.Dict[str, typing.Any]]] = pydantic.Field(default=None) """ An empty list of articles, as no matches were found. """ diff --git a/src/newscatcher/types/failed_search_similar_response_dto.py b/src/newscatcher/types/failed_search_similar_response_dto.py deleted file mode 100644 index 2416b4a..0000000 --- a/src/newscatcher/types/failed_search_similar_response_dto.py +++ /dev/null @@ -1,30 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2 -from .base_search_response_dto import BaseSearchResponseDto -from .user_input_dto import UserInputDto - - -class FailedSearchSimilarResponseDto(BaseSearchResponseDto): - """ - The response model for a failed `Search Similar` request. - """ - - articles: typing.Optional[typing.List[typing.Dict[str, typing.Optional[typing.Any]]]] = pydantic.Field(default=None) - """ - An empty list of articles, as no matches were found. - """ - - user_input: typing.Optional[UserInputDto] = None - - 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/types/include_similar_documents.py b/src/newscatcher/types/include_similar_documents.py deleted file mode 100644 index f763984..0000000 --- a/src/newscatcher/types/include_similar_documents.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -IncludeSimilarDocuments = bool diff --git a/src/newscatcher/types/nlp_data_entity.py b/src/newscatcher/types/nlp_data_entity.py index 7bff2b1..98bc8d6 100644 --- a/src/newscatcher/types/nlp_data_entity.py +++ b/src/newscatcher/types/nlp_data_entity.py @@ -35,65 +35,72 @@ class NlpDataEntity(UniversalBaseModel): """ A dense 1024-dimensional vector representation of the article content, generated using the [multilingual-e5-large](https://huggingface.co/intfloat/multilingual-e5-large) model. - **Note**: The `new_embedding` field is only available in the `v3_local_news_nlp_embeddings` subscription plan. - """ - - ner_per: typing_extensions.Annotated[typing.Optional[NamedEntityList], FieldMetadata(alias="ner_PER")] = ( - pydantic.Field(default=None) - ) - """ - Named Entity Recognition for person entities (individuals' names). - """ - - ner_org: typing_extensions.Annotated[typing.Optional[NamedEntityList], FieldMetadata(alias="ner_ORG")] = ( - pydantic.Field(default=None) - ) - """ - Named Entity Recognition for organization entities (company names, institutions). - """ - - ner_misc: typing_extensions.Annotated[typing.Optional[NamedEntityList], FieldMetadata(alias="ner_MISC")] = ( - pydantic.Field(default=None) - ) - """ - Named Entity Recognition for miscellaneous entities (events, nationalities, products). - """ - - ner_loc: typing_extensions.Annotated[typing.Optional[NamedEntityList], FieldMetadata(alias="ner_LOC")] = ( - pydantic.Field(default=None) - ) - """ - Named Entity Recognition for location entities (cities, countries, geographic features). - """ - + **Note**: The `new_embedding` field is only available in the `v3_nlp_embeddings` subscription plan. + """ + + ner_per: typing_extensions.Annotated[ + typing.Optional[NamedEntityList], + FieldMetadata(alias="ner_PER"), + pydantic.Field( + alias="ner_PER", description="Named Entity Recognition for person entities (individuals' names)." + ), + ] = None + ner_org: typing_extensions.Annotated[ + typing.Optional[NamedEntityList], + FieldMetadata(alias="ner_ORG"), + pydantic.Field( + alias="ner_ORG", + description="Named Entity Recognition for organization entities (company names, institutions).", + ), + ] = None + ner_misc: typing_extensions.Annotated[ + typing.Optional[NamedEntityList], + FieldMetadata(alias="ner_MISC"), + pydantic.Field( + alias="ner_MISC", + description="Named Entity Recognition for miscellaneous entities (events, nationalities, products).", + ), + ] = None + ner_loc: typing_extensions.Annotated[ + typing.Optional[NamedEntityList], + FieldMetadata(alias="ner_LOC"), + pydantic.Field( + alias="ner_LOC", + description="Named Entity Recognition for location entities (cities, countries, geographic features).", + ), + ] = None translation_ner_per: typing_extensions.Annotated[ - typing.Optional[NamedEntityList], FieldMetadata(alias="translation_ner_PER") - ] = pydantic.Field(default=None) - """ - Named Entity Recognition for person entities (individuals' names) extracted from the English translation of the article. - """ - + typing.Optional[NamedEntityList], + FieldMetadata(alias="translation_ner_PER"), + pydantic.Field( + alias="translation_ner_PER", + description="Named Entity Recognition for person entities (individuals' names) extracted from the English translation of the article.", + ), + ] = None translation_ner_org: typing_extensions.Annotated[ - typing.Optional[NamedEntityList], FieldMetadata(alias="translation_ner_ORG") - ] = pydantic.Field(default=None) - """ - Named Entity Recognition for organization entities (company names, institutions) extracted from the English translation of the article. - """ - + typing.Optional[NamedEntityList], + FieldMetadata(alias="translation_ner_ORG"), + pydantic.Field( + alias="translation_ner_ORG", + description="Named Entity Recognition for organization entities (company names, institutions) extracted from the English translation of the article.", + ), + ] = None translation_ner_misc: typing_extensions.Annotated[ - typing.Optional[NamedEntityList], FieldMetadata(alias="translation_ner_MISC") - ] = pydantic.Field(default=None) - """ - Named Entity Recognition for miscellaneous entities (events, nationalities, products) extracted from the English translation of the article. - """ - + typing.Optional[NamedEntityList], + FieldMetadata(alias="translation_ner_MISC"), + pydantic.Field( + alias="translation_ner_MISC", + description="Named Entity Recognition for miscellaneous entities (events, nationalities, products) extracted from the English translation of the article.", + ), + ] = None translation_ner_loc: typing_extensions.Annotated[ - typing.Optional[NamedEntityList], FieldMetadata(alias="translation_ner_LOC") - ] = pydantic.Field(default=None) - """ - Named Entity Recognition for location entities (cities, countries, geographic features) extracted from the English translation of the article. - """ - + typing.Optional[NamedEntityList], + FieldMetadata(alias="translation_ner_LOC"), + pydantic.Field( + alias="translation_ner_LOC", + description="Named Entity Recognition for location entities (cities, countries, geographic features) extracted from the English translation of the article.", + ), + ] = None iptc_tags_name: typing.Optional[typing.List[str]] = pydantic.Field(default=None) """ IPTC media topic taxonomy paths identified in the article content. Each path represents a hierarchical category following the IPTC standard. diff --git a/src/newscatcher/types/not_theme.py b/src/newscatcher/types/not_theme.py index 692ef9e..9c08b42 100644 --- a/src/newscatcher/types/not_theme.py +++ b/src/newscatcher/types/not_theme.py @@ -1,5 +1,3 @@ # This file was auto-generated by Fern from our API Definition. -import typing - -NotTheme = typing.Union[str, typing.List[str]] +NotTheme = str diff --git a/src/newscatcher/types/published_date_precision.py b/src/newscatcher/types/published_date_precision.py index 3f7dff7..d5d7895 100644 --- a/src/newscatcher/types/published_date_precision.py +++ b/src/newscatcher/types/published_date_precision.py @@ -1,5 +1,3 @@ # This file was auto-generated by Fern from our API Definition. -import typing - -PublishedDatePrecision = typing.Union[typing.Literal["full", "timezone unknown", "date"], typing.Any] +PublishedDatePrecision = str diff --git a/src/newscatcher/types/search_response_dto.py b/src/newscatcher/types/search_response_dto.py index f0d7abb..0d08579 100644 --- a/src/newscatcher/types/search_response_dto.py +++ b/src/newscatcher/types/search_response_dto.py @@ -4,7 +4,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2 -from .articles import Articles +from .articles_dto import ArticlesDto from .base_search_response_dto import BaseSearchResponseDto from .user_input_dto import UserInputDto @@ -18,7 +18,7 @@ class SearchResponseDto(BaseSearchResponseDto): - The `nlp` property within the article object `articles[n].nlp` is only available with NLP-enabled subscription plans. """ - articles: typing.Optional[Articles] = None + articles: typing.Optional[ArticlesDto] = None user_input: typing.Optional[UserInputDto] = None if IS_PYDANTIC_V2: diff --git a/src/newscatcher/types/search_similar_response_dto.py b/src/newscatcher/types/search_similar_response_dto.py deleted file mode 100644 index b9d47f5..0000000 --- a/src/newscatcher/types/search_similar_response_dto.py +++ /dev/null @@ -1,35 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2 -from .base_search_response_dto import BaseSearchResponseDto -from .similar_article_entity import SimilarArticleEntity -from .user_input_dto import UserInputDto - - -class SearchSimilarResponseDto(BaseSearchResponseDto): - """ - The response model for a successful `Search similar` request. Response field behavior: - - Required fields are guaranteed to be present and non-null. - - Optional fields may be `null` or `undefined` if the data point is not presented or couldn't be extracted during processing. - - To access article properties in the `articles` response array, use array index notation. For example, `articles[n].title`, where `n` is the zero-based index of the article object (0, 1, 2, etc.). - - The `nlp` property within the article object `articles[n].nlp` is only available with NLP-enabled subscription plans. - """ - - articles: typing.Optional[typing.List[SimilarArticleEntity]] = pydantic.Field(default=None) - """ - A list of articles matching the search criteria. - """ - - user_input: typing.Optional[UserInputDto] = None - - 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/types/similar_article_entity.py b/src/newscatcher/types/similar_article_entity.py deleted file mode 100644 index 960a793..0000000 --- a/src/newscatcher/types/similar_article_entity.py +++ /dev/null @@ -1,30 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2 -from .article_entity import ArticleEntity -from .similar_document import SimilarDocument - - -class SimilarArticleEntity(ArticleEntity): - """ - The data model for an article result in the `Search similar` articles request. Response field behavior: - - Required fields are guaranteed to be present and non-null. - - Optional fields may be `null` or `undefined` if the data point is not presented or couldn't be extracted during processing. - """ - - similar_documents: typing.Optional[typing.List[SimilarDocument]] = pydantic.Field(default=None) - """ - A list of documents similar to the article. - """ - - 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/types/similar_documents_number.py b/src/newscatcher/types/similar_documents_number.py deleted file mode 100644 index aea11e3..0000000 --- a/src/newscatcher/types/similar_documents_number.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -SimilarDocumentsNumber = int diff --git a/src/newscatcher/types/sources_response_dto.py b/src/newscatcher/types/sources_response_dto.py index e06ba3e..1565fa8 100644 --- a/src/newscatcher/types/sources_response_dto.py +++ b/src/newscatcher/types/sources_response_dto.py @@ -24,7 +24,7 @@ class SourcesResponseDto(UniversalBaseModel): A list of news sources that match the specified criteria. """ - user_input: typing.Dict[str, typing.Optional[typing.Any]] = pydantic.Field() + user_input: typing.Dict[str, typing.Any] = pydantic.Field() """ The user input parameters for the request. """ diff --git a/src/newscatcher/types/theme.py b/src/newscatcher/types/theme.py index e25f554..10f0229 100644 --- a/src/newscatcher/types/theme.py +++ b/src/newscatcher/types/theme.py @@ -1,5 +1,3 @@ # This file was auto-generated by Fern from our API Definition. -import typing - -Theme = typing.Union[str, typing.List[str]] +Theme = str diff --git a/src/newscatcher/types/user_input_dto.py b/src/newscatcher/types/user_input_dto.py index f5da8ea..bec72e4 100644 --- a/src/newscatcher/types/user_input_dto.py +++ b/src/newscatcher/types/user_input_dto.py @@ -2,4 +2,4 @@ import typing -UserInputDto = typing.Dict[str, typing.Optional[typing.Any]] +UserInputDto = typing.Dict[str, typing.Any] diff --git a/tests/utils/test_http_client.py b/tests/utils/test_http_client.py index 07e534a..2bccf94 100644 --- a/tests/utils/test_http_client.py +++ b/tests/utils/test_http_client.py @@ -1,13 +1,57 @@ # This file was auto-generated by Fern from our API Definition. -from newscatcher.core.http_client import get_request_body +from typing import Any, Dict + +import pytest + +from newscatcher.core.http_client import ( + AsyncHttpClient, + HttpClient, + _build_url, + get_request_body, + remove_none_from_dict, +) from newscatcher.core.request_options import RequestOptions +# Stub clients for testing HttpClient and AsyncHttpClient +class _DummySyncClient: + """A minimal stub for httpx.Client that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyAsyncClient: + """A minimal stub for httpx.AsyncClient that records request arguments.""" + + def __init__(self) -> None: + self.last_request_kwargs: Dict[str, Any] = {} + + async def request(self, **kwargs: Any) -> "_DummyResponse": + self.last_request_kwargs = kwargs + return _DummyResponse() + + +class _DummyResponse: + """A minimal stub for httpx.Response.""" + + status_code = 200 + headers: Dict[str, str] = {} + + def get_request_options() -> RequestOptions: return {"additional_body_parameters": {"see you": "later"}} +def get_request_options_with_none() -> RequestOptions: + return {"additional_body_parameters": {"see you": "later", "optional": None}} + + def test_get_json_request_body() -> None: json_body, data_body = get_request_body(json={"hello": "world"}, data=None, request_options=None, omit=None) assert json_body == {"hello": "world"} @@ -48,14 +92,209 @@ def test_get_none_request_body() -> None: def test_get_empty_json_request_body() -> None: + """Test that implicit empty bodies (json=None) are collapsed to None.""" unrelated_request_options: RequestOptions = {"max_retries": 3} json_body, data_body = get_request_body(json=None, data=None, request_options=unrelated_request_options, omit=None) assert json_body is None assert data_body is None - json_body_extras, data_body_extras = get_request_body( - json={}, data=None, request_options=unrelated_request_options, omit=None + +def test_explicit_empty_json_body_is_preserved() -> None: + """Test that explicit empty bodies (json={}) are preserved and sent as {}. + + This is important for endpoints where the request body is required but all + fields are optional. The server expects valid JSON ({}) not an empty body. + """ + unrelated_request_options: RequestOptions = {"max_retries": 3} + + # Explicit json={} should be preserved + json_body, data_body = get_request_body(json={}, data=None, request_options=unrelated_request_options, omit=None) + assert json_body == {} + assert data_body is None + + # Explicit data={} should also be preserved + json_body2, data_body2 = get_request_body(json=None, data={}, request_options=unrelated_request_options, omit=None) + assert json_body2 is None + assert data_body2 == {} + + +def test_json_body_preserves_none_values() -> None: + """Test that JSON bodies preserve None values (they become JSON null).""" + json_body, data_body = get_request_body( + json={"hello": "world", "optional": None}, data=None, request_options=None, omit=None ) + # JSON bodies should preserve None values + assert json_body == {"hello": "world", "optional": None} + assert data_body is None - assert json_body_extras is None - assert data_body_extras is None + +def test_data_body_preserves_none_values_without_multipart() -> None: + """Test that data bodies preserve None values when not using multipart. + + The filtering of None values happens in HttpClient.request/stream methods, + not in get_request_body. This test verifies get_request_body doesn't filter None. + """ + json_body, data_body = get_request_body( + json=None, data={"hello": "world", "optional": None}, request_options=None, omit=None + ) + # get_request_body should preserve None values in data body + # The filtering happens later in HttpClient.request when multipart is detected + assert data_body == {"hello": "world", "optional": None} + assert json_body is None + + +def test_remove_none_from_dict_filters_none_values() -> None: + """Test that remove_none_from_dict correctly filters out None values.""" + original = {"hello": "world", "optional": None, "another": "value", "also_none": None} + filtered = remove_none_from_dict(original) + assert filtered == {"hello": "world", "another": "value"} + # Original should not be modified + assert original == {"hello": "world", "optional": None, "another": "value", "also_none": None} + + +def test_remove_none_from_dict_empty_dict() -> None: + """Test that remove_none_from_dict handles empty dict.""" + assert remove_none_from_dict({}) == {} + + +def test_remove_none_from_dict_all_none() -> None: + """Test that remove_none_from_dict handles dict with all None values.""" + assert remove_none_from_dict({"a": None, "b": None}) == {} + + +def test_http_client_does_not_pass_empty_params_list() -> None: + """Test that HttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + ) + + # Use a path with query params (e.g., pagination cursor URL) + http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +def test_http_client_passes_encoded_params_when_present() -> None: + """Test that HttpClient passes encoded params when params are provided.""" + dummy_client = _DummySyncClient() + http_client = HttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + ) + + http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +@pytest.mark.asyncio +async def test_async_http_client_does_not_pass_empty_params_list() -> None: + """Test that AsyncHttpClient passes params=None when params are empty. + + This prevents httpx from stripping existing query parameters from the URL, + which happens when params=[] or params={} is passed. + """ + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com", + async_base_headers=None, + ) + + # Use a path with query params (e.g., pagination cursor URL) + await http_client.request( + path="resource?after=123", + method="GET", + params=None, + request_options=None, + ) + + # We care that httpx receives params=None, not [] or {} + assert "params" in dummy_client.last_request_kwargs + assert dummy_client.last_request_kwargs["params"] is None + + # Verify the query string in the URL is preserved + url = str(dummy_client.last_request_kwargs["url"]) + assert "after=123" in url, f"Expected query param 'after=123' in URL, got: {url}" + + +@pytest.mark.asyncio +async def test_async_http_client_passes_encoded_params_when_present() -> None: + """Test that AsyncHttpClient passes encoded params when params are provided.""" + dummy_client = _DummyAsyncClient() + http_client = AsyncHttpClient( + httpx_client=dummy_client, # type: ignore[arg-type] + base_timeout=lambda: None, + base_headers=lambda: {}, + base_url=lambda: "https://example.com/resource", + async_base_headers=None, + ) + + await http_client.request( + path="", + method="GET", + params={"after": "456"}, + request_options=None, + ) + + params = dummy_client.last_request_kwargs["params"] + # For a simple dict, encode_query should give a single (key, value) tuple + assert params == [("after", "456")] + + +def test_basic_url_joining() -> None: + """Test basic URL joining with a simple base URL and path.""" + result = _build_url("https://api.example.com", "/users") + assert result == "https://api.example.com/users" + + +def test_basic_url_joining_trailing_slash() -> None: + """Test basic URL joining with a simple base URL and path.""" + result = _build_url("https://api.example.com/", "/users") + assert result == "https://api.example.com/users" + + +def test_preserves_base_url_path_prefix() -> None: + """Test that path prefixes in base URL are preserved. + + This is the critical bug fix - urllib.parse.urljoin() would strip + the path prefix when the path starts with '/'. + """ + result = _build_url("https://cloud.example.com/org/tenant/api", "/users") + assert result == "https://cloud.example.com/org/tenant/api/users" + + +def test_preserves_base_url_path_prefix_trailing_slash() -> None: + """Test that path prefixes in base URL are preserved.""" + result = _build_url("https://cloud.example.com/org/tenant/api/", "/users") + assert result == "https://cloud.example.com/org/tenant/api/users" diff --git a/tests/utils/test_query_encoding.py b/tests/utils/test_query_encoding.py index 3a3919e..b6a0971 100644 --- a/tests/utils/test_query_encoding.py +++ b/tests/utils/test_query_encoding.py @@ -1,6 +1,5 @@ # This file was auto-generated by Fern from our API Definition. - from newscatcher.core.query_encoder import encode_query