From 3d28c21541ce2450d3e52abb659ffb7b09a56b54 Mon Sep 17 00:00:00 2001 From: Esteban Vincent Date: Thu, 28 May 2026 18:06:24 +0200 Subject: [PATCH 1/3] feat(client): add custom auth header name support Some API gateways (e.g. Azure APIM) require the key in a non-standard header like `Ocp-Apim-Subscription-Key` instead of `Authorization: Bearer`. `auth_header` lets callers override the header name; the key value is sent as-is when set. --- src/linkup/_client.py | 11 ++++++++++- tests/unit/client_test.py | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/linkup/_client.py b/src/linkup/_client.py index 0547be5..ead4b91 100644 --- a/src/linkup/_client.py +++ b/src/linkup/_client.py @@ -59,6 +59,10 @@ class LinkupClient: x402_signer: An optional x402 signer for payment-gated endpoints. If provided, the client will attempt to handle 402 responses automatically. Cannot be used together with api_key. + auth_header: Custom header name to use for the API key (e.g. + ``"Ocp-Apim-Subscription-Key"``). When set, the API key value is sent as + ``: `` instead of the default + ``Authorization: Bearer ``. Raises: ValueError: If the API key is not provided and not found in the environment variable. @@ -72,6 +76,7 @@ def __init__( api_key: str | SecretStr | None = None, base_url: str = "https://api.linkup.so/v1", x402_signer: LinkupX402Signer | None = None, + auth_header: str | None = None, ) -> None: if api_key is not None and x402_signer is not None: raise ValueError("Cannot provide both api_key and x402_signer") @@ -90,6 +95,7 @@ def __init__( self._api_key = api_key self._base_url: str = base_url + self._auth_header: str | None = auth_header def search( self, @@ -889,7 +895,10 @@ def _user_agent(self) -> str: # pragma: no cover def _headers(self) -> dict[str, str]: # pragma: no cover headers: dict[str, str] = {"User-Agent": self._user_agent()} if self._api_key is not None: - headers["Authorization"] = f"Bearer {self._api_key.get_secret_value()}" + if self._auth_header is not None: + headers[self._auth_header] = self._api_key.get_secret_value() + else: + headers["Authorization"] = f"Bearer {self._api_key.get_secret_value()}" return headers def _request( diff --git a/tests/unit/client_test.py b/tests/unit/client_test.py index 92941f3..ec75e7d 100644 --- a/tests/unit/client_test.py +++ b/tests/unit/client_test.py @@ -1405,6 +1405,26 @@ def test_client_both_api_key_and_x402_signer_raises( linkup.Client(api_key="test-key", x402_signer=mock_x402_signer) +def test_client_custom_auth_header( + mocker: MockerFixture, +) -> None: + client = linkup.Client(api_key="my-key", auth_header="Ocp-Apim-Subscription-Key") + assert client._auth_header == "Ocp-Apim-Subscription-Key" # noqa: SLF001 + + client_mock = mocker.patch("httpx.Client") + client_mock.return_value.__enter__ = lambda s: s + client_mock.return_value.__exit__ = MagicMock(return_value=False) + client_mock.return_value.request.return_value = Response( + status_code=200, content=b'{"results": []}' + ) + client.search(query="query", depth="standard", output_type="searchResults") + + init_kwargs = client_mock.call_args[1] + assert "Ocp-Apim-Subscription-Key" in init_kwargs["headers"] + assert init_kwargs["headers"]["Ocp-Apim-Subscription-Key"] == "my-key" + assert "Authorization" not in init_kwargs["headers"] + + def test_client_x402_no_auth_header( mocker: MockerFixture, x402_client: linkup.Client, From ff47f0f0f079019feb0aae80c7c1033213effece Mon Sep 17 00:00:00 2001 From: Esteban Vincent Date: Thu, 28 May 2026 18:16:58 +0200 Subject: [PATCH 2/3] test(client): fix custom auth header mock using return_value --- tests/unit/client_test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/unit/client_test.py b/tests/unit/client_test.py index ec75e7d..14d50f3 100644 --- a/tests/unit/client_test.py +++ b/tests/unit/client_test.py @@ -1412,8 +1412,7 @@ def test_client_custom_auth_header( assert client._auth_header == "Ocp-Apim-Subscription-Key" # noqa: SLF001 client_mock = mocker.patch("httpx.Client") - client_mock.return_value.__enter__ = lambda s: s - client_mock.return_value.__exit__ = MagicMock(return_value=False) + client_mock.return_value.__enter__.return_value = client_mock.return_value client_mock.return_value.request.return_value = Response( status_code=200, content=b'{"results": []}' ) From 9b22f4709b48955a267d66dd95c8689bf654cc07 Mon Sep 17 00:00:00 2001 From: Esteban Vincent Date: Fri, 29 May 2026 10:17:15 +0200 Subject: [PATCH 3/3] test(client): remove private attr check in custom auth header test --- tests/unit/client_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/client_test.py b/tests/unit/client_test.py index 14d50f3..840a727 100644 --- a/tests/unit/client_test.py +++ b/tests/unit/client_test.py @@ -1409,7 +1409,6 @@ def test_client_custom_auth_header( mocker: MockerFixture, ) -> None: client = linkup.Client(api_key="my-key", auth_header="Ocp-Apim-Subscription-Key") - assert client._auth_header == "Ocp-Apim-Subscription-Key" # noqa: SLF001 client_mock = mocker.patch("httpx.Client") client_mock.return_value.__enter__.return_value = client_mock.return_value