diff --git a/src/linkup/__init__.py b/src/linkup/__init__.py index 5ed5a05..a6173d1 100644 --- a/src/linkup/__init__.py +++ b/src/linkup/__init__.py @@ -9,6 +9,7 @@ LinkupFetchUrlIsFileError, LinkupInsufficientCreditError, LinkupInvalidRequestError, + LinkupIpNotWhitelistedError, LinkupNoResultError, LinkupPaymentRequiredError, LinkupTaskNotFoundError, @@ -58,6 +59,7 @@ FetchUrlIsFileError = LinkupFetchUrlIsFileError InsufficientCreditError = LinkupInsufficientCreditError InvalidRequestError = LinkupInvalidRequestError +IpNotWhitelistedError = LinkupIpNotWhitelistedError NoResultError = LinkupNoResultError PaymentRequiredError = LinkupPaymentRequiredError ResearchTask = LinkupResearchTask @@ -98,6 +100,7 @@ "FetchUrlIsFileError", "InsufficientCreditError", "InvalidRequestError", + "IpNotWhitelistedError", "JSONObject", "LinkupAuthenticationError", "LinkupBudgetLimitExceededError", @@ -113,6 +116,7 @@ "LinkupFetchUrlIsFileError", "LinkupInsufficientCreditError", "LinkupInvalidRequestError", + "LinkupIpNotWhitelistedError", "LinkupNoResultError", "LinkupPaymentRequiredError", "LinkupResearchTask", diff --git a/src/linkup/_client.py b/src/linkup/_client.py index fb299fe..be51fde 100644 --- a/src/linkup/_client.py +++ b/src/linkup/_client.py @@ -19,6 +19,7 @@ LinkupFetchUrlIsFileError, LinkupInsufficientCreditError, LinkupInvalidRequestError, + LinkupIpNotWhitelistedError, LinkupNoResultError, LinkupPaymentRequiredError, LinkupTaskNotFoundError, @@ -1361,6 +1362,12 @@ def _raise_linkup_error(self, response: httpx.Response) -> None: f"Original error message: {error_msg}." ) if response.status_code == 403: + if code == "IP_NOT_WHITELISTED": + raise LinkupIpNotWhitelistedError( + "The Linkup API rejected the request IP address (403). Make sure it is " + "included in the API key's IP whitelist.\n" + f"Original error message: {error_msg}." + ) if code == "TASK_TYPE_NOT_SUPPORTED": raise LinkupUnsupportedTaskTypeError( "The Linkup API returned an unsupported task type error (403). \n" diff --git a/src/linkup/_errors.py b/src/linkup/_errors.py index a0721ae..4890895 100644 --- a/src/linkup/_errors.py +++ b/src/linkup/_errors.py @@ -29,6 +29,12 @@ class LinkupAuthenticationError(Exception): pass +class LinkupIpNotWhitelistedError(Exception): + """IP whitelist error, raised when the request IP is not allowed by the API key.""" + + pass + + class LinkupPaymentRequiredError(Exception): """Payment required error, raised when the Linkup API returns a 402 status code. diff --git a/tests/unit/client_test.py b/tests/unit/client_test.py index 50c3d74..5c78bb1 100644 --- a/tests/unit/client_test.py +++ b/tests/unit/client_test.py @@ -474,6 +474,19 @@ async def test_async_search_structured_output_requires_schema(client: linkup.Cli """, linkup.AuthenticationError, ), + ( + 403, + b""" + { + "error": { + "code": "IP_NOT_WHITELISTED", + "message": "IP address 192.0.2.1 is not whitelisted", + "details": [] + } + } + """, + linkup.IpNotWhitelistedError, + ), ( 403, b"""