Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/linkup/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
LinkupInsufficientCreditError,
LinkupInvalidRequestError,
LinkupNoResultError,
LinkupTimeoutError,
LinkupTooManyRequestsError,
LinkupUnknownError,
)
Expand Down Expand Up @@ -33,6 +34,7 @@
"LinkupSearchTextResult",
"LinkupSource",
"LinkupSourcedAnswer",
"LinkupTimeoutError",
"LinkupTooManyRequestsError",
"LinkupUnknownError",
"__version__",
Expand Down
85 changes: 61 additions & 24 deletions src/linkup/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
LinkupInsufficientCreditError,
LinkupInvalidRequestError,
LinkupNoResultError,
LinkupTimeoutError,
LinkupTooManyRequestsError,
LinkupUnknownError,
)
Expand Down Expand Up @@ -71,6 +72,7 @@ def search(
max_results: int | None = None,
include_inline_citations: bool | None = None,
include_sources: bool | None = None,
timeout: float | None = None,
) -> Any: # noqa: ANN401
"""Perform a web search using the Linkup API `search` endpoint.

Expand Down Expand Up @@ -101,6 +103,8 @@ def search(
answer should include inline citations.
include_sources: If output_type is "structured", indicate whether the answer should
include sources. This will modify the schema of the structured response.
timeout: The timeout for the HTTP request, in seconds. If None, the request will have
no timeout.

Returns:
The Linkup API search result, which can have different types based on the parameters:
Expand All @@ -120,6 +124,7 @@ def search(
LinkupAuthenticationError: If the Linkup API key is invalid.
LinkupInsufficientCreditError: If you have run out of credit.
LinkupNoResultError: If the search query did not yield any result.
LinkupTimeoutError: If the request times out.
"""
params: dict[str, str | bool | int | list[str]] = self._get_search_params(
query=query,
Expand All @@ -136,12 +141,17 @@ def search(
include_sources=include_sources,
)

response: httpx.Response = self._request(
method="POST",
url="/search",
json=params,
timeout=None,
)
try:
response: httpx.Response = self._request(
method="POST",
url="/search",
json=params,
timeout=timeout,
)
except httpx.TimeoutException as e:
raise LinkupTimeoutError(
"The request to the Linkup API timed out. Try increasing the timeout value."
) from e
if response.status_code != 200:
self._raise_linkup_error(response=response)

Expand All @@ -166,6 +176,7 @@ async def async_search(
max_results: int | None = None,
include_inline_citations: bool | None = None,
include_sources: bool | None = None,
timeout: float | None = None,
) -> Any: # noqa: ANN401
"""Asynchronously perform a web search using the Linkup API `search` endpoint.

Expand Down Expand Up @@ -196,6 +207,8 @@ async def async_search(
answer should include inline citations.
include_sources: If output_type is "structured", indicate whether the answer should
include sources. This will modify the schema of the structured response.
timeout: The timeout for the HTTP request, in seconds. If None, the request will have
no timeout.

Returns:
The Linkup API search result, which can have different types based on the parameters:
Expand All @@ -215,6 +228,7 @@ async def async_search(
LinkupAuthenticationError: If the Linkup API key is invalid.
LinkupInsufficientCreditError: If you have run out of credit.
LinkupNoResultError: If the search query did not yield any result.
LinkupTimeoutError: If the request times out.
"""
params: dict[str, str | bool | int | list[str]] = self._get_search_params(
query=query,
Expand All @@ -231,12 +245,17 @@ async def async_search(
include_sources=include_sources,
)

response: httpx.Response = await self._async_request(
method="POST",
url="/search",
json=params,
timeout=None,
)
try:
response: httpx.Response = await self._async_request(
method="POST",
url="/search",
json=params,
timeout=timeout,
)
except httpx.TimeoutException as e:
raise LinkupTimeoutError(
"The request to the Linkup API timed out. Try increasing the timeout value."
) from e
if response.status_code != 200:
self._raise_linkup_error(response=response)

Expand All @@ -253,6 +272,7 @@ def fetch(
include_raw_html: bool | None = None,
render_js: bool | None = None,
extract_images: bool | None = None,
timeout: float | None = None,
) -> LinkupFetchResponse:
"""Fetch the content of a web page using the Linkup API `fetch` endpoint.

Expand All @@ -266,13 +286,16 @@ def fetch(
render_js: Whether the API should render the JavaScript of the webpage.
extract_images: Whether the API should extract images from the webpage and return them
in the response.
timeout: The timeout for the HTTP request, in seconds. If None, the request will have
no timeout.

Returns:
The response of the web page fetch, containing the web page content.

Raises:
LinkupInvalidRequestError: If the provided URL is not valid.
LinkupFailedFetchError: If the provided URL is not found or can't be fetched.
LinkupTimeoutError: If the request times out.
"""
params: dict[str, str | bool] = self._get_fetch_params(
url=url,
Expand All @@ -281,12 +304,17 @@ def fetch(
extract_images=extract_images,
)

response: httpx.Response = self._request(
method="POST",
url="/fetch",
json=params,
timeout=None,
)
try:
response: httpx.Response = self._request(
method="POST",
url="/fetch",
json=params,
timeout=timeout,
)
except httpx.TimeoutException as e:
raise LinkupTimeoutError(
"The request to the Linkup API timed out. Try increasing the timeout value."
) from e
if response.status_code != 200:
self._raise_linkup_error(response=response)

Expand All @@ -298,6 +326,7 @@ async def async_fetch(
include_raw_html: bool | None = None,
render_js: bool | None = None,
extract_images: bool | None = None,
timeout: float | None = None,
) -> LinkupFetchResponse:
"""Asynchronously fetch the content of a web page using the Linkup API `fetch` endpoint.

Expand All @@ -311,13 +340,16 @@ async def async_fetch(
render_js: Whether the API should render the JavaScript of the webpage.
extract_images: Whether the API should extract images from the webpage and return them
in the response.
timeout: The timeout for the HTTP request, in seconds. If None, the request will have
no timeout.

Returns:
The response of the web page fetch, containing the web page content.

Raises:
LinkupInvalidRequestError: If the provided URL is not valid.
LinkupFailedFetchError: If the provided URL is not found or can't be fetched.
LinkupTimeoutError: If the request times out.
"""
params: dict[str, str | bool] = self._get_fetch_params(
url=url,
Expand All @@ -326,12 +358,17 @@ async def async_fetch(
extract_images=extract_images,
)

response: httpx.Response = await self._async_request(
method="POST",
url="/fetch",
json=params,
timeout=None,
)
try:
response: httpx.Response = await self._async_request(
method="POST",
url="/fetch",
json=params,
timeout=timeout,
)
except httpx.TimeoutException as e:
raise LinkupTimeoutError(
"The request to the Linkup API timed out. Try increasing the timeout value."
) from e
if response.status_code != 200:
self._raise_linkup_error(response=response)

Expand Down
9 changes: 9 additions & 0 deletions src/linkup/_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ class LinkupFailedFetchError(Exception):
pass


class LinkupTimeoutError(Exception):
"""Timeout error, raised when the HTTP request to the Linkup API times out.

It is raised when a timeout is specified and the request exceeds the given duration.
"""

pass


class LinkupUnknownError(Exception):
"""Unknown error, raised when the Linkup API returns an unknown status code."""

Expand Down
Loading