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: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ When adding or changing a public API capability, update the relevant pieces toge
- request/response typing and models,
- sync and async behavior when applicable,
- tests,
- README if the user-facing API changed.
- README if anything in it became out-dated.

## Validation

Expand Down
56 changes: 32 additions & 24 deletions src/linkup/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ def search(
output_type: Literal["searchResults", "sourcedAnswer", "structured"],
structured_output_schema: type[BaseModel] | dict[str, Any] | str | None = None,
include_images: bool | None = None,
from_date: date | None = None,
to_date: date | None = None,
from_date: date | str | None = None,
to_date: date | str | None = None,
exclude_domains: list[str] | None = None,
include_domains: list[str] | None = None,
max_results: int | None = None,
Expand Down Expand Up @@ -133,10 +133,12 @@ def search(
output. Supported formats are a pydantic.BaseModel, a Python dictionary containing a
valid object JSON schema, or a string representing a valid object JSON schema.
include_images: Indicate whether images should be included during the search.
from_date: The date from which the search results should be considered. If None, the
search results will not be filtered by date.
to_date: The date until which the search results should be considered. If None, the
search results will not be filtered by date.
from_date: The date from which the search results should be considered. Accepts a
`datetime.date`, `YYYY-MM-DD`, or full ISO datetime string. If None, the search
results will not be filtered by date.
to_date: The date until which the search results should be considered. Accepts a
`datetime.date`, `YYYY-MM-DD`, or full ISO datetime string. If None, the search
results will not be filtered by date.
exclude_domains: If you want to exclude specific domains from your search.
include_domains: If you want the search to only return results from certain domains.
max_results: The maximum number of results to return.
Expand Down Expand Up @@ -204,8 +206,8 @@ async def async_search(
output_type: Literal["searchResults", "sourcedAnswer", "structured"],
structured_output_schema: type[BaseModel] | dict[str, Any] | str | None = None,
include_images: bool | None = None,
from_date: date | None = None,
to_date: date | None = None,
from_date: date | str | None = None,
to_date: date | str | None = None,
exclude_domains: list[str] | None = None,
include_domains: list[str] | None = None,
max_results: int | None = None,
Expand Down Expand Up @@ -233,10 +235,12 @@ async def async_search(
output. Supported formats are a pydantic.BaseModel, a Python dictionary containing a
valid object JSON schema, or a string representing a valid object JSON schema.
include_images: Indicate whether images should be included during the search.
from_date: The date from which the search results should be considered. If None, the
search results will not be filtered by date.
to_date: The date until which the search results should be considered. If None, the
search results will not be filtered by date.
from_date: The date from which the search results should be considered. Accepts a
`datetime.date`, `YYYY-MM-DD`, or full ISO datetime string. If None, the search
results will not be filtered by date.
to_date: The date until which the search results should be considered. Accepts a
`datetime.date`, `YYYY-MM-DD`, or full ISO datetime string. If None, the search
results will not be filtered by date.
exclude_domains: If you want to exclude specific domains from your search.
include_domains: If you want the search to only return results from certain domains.
max_results: The maximum number of results to return.
Expand Down Expand Up @@ -304,8 +308,8 @@ def research(
reasoning_depth: Literal["S", "M", "L", "XL"] | None = None,
mode: Literal["answer", "auto", "investigate", "research"] | None = None,
structured_output_schema: type[BaseModel] | dict[str, Any] | str | None = None,
from_date: date | None = None,
to_date: date | None = None,
from_date: date | str | None = None,
to_date: date | str | None = None,
exclude_domains: list[str] | None = None,
include_domains: list[str] | None = None,
timeout: float | None = None,
Expand All @@ -325,10 +329,12 @@ def research(
structured_output_schema: If output_type is "structured", specify the output schema.
Supported formats are a pydantic.BaseModel, a Python dictionary containing a valid
object JSON schema, or a string representing a valid object JSON schema.
from_date: The date from which the research sources should be considered. If None,
sources will not be filtered by a start date.
to_date: The date until which the research sources should be considered. If None,
sources will not be filtered by an end date.
from_date: The date from which the research sources should be considered. Accepts a
`datetime.date`, `YYYY-MM-DD`, or full ISO datetime string. If None, sources will
not be filtered by a start date.
to_date: The date until which the research sources should be considered. Accepts a
`datetime.date`, `YYYY-MM-DD`, or full ISO datetime string. If None, sources will
not be filtered by an end date.
exclude_domains: Domains to exclude from the research sources.
include_domains: Domains to restrict the research sources to.
timeout: The timeout for the HTTP request, in seconds. If None, the request will have
Expand Down Expand Up @@ -373,8 +379,8 @@ async def async_research(
reasoning_depth: Literal["S", "M", "L", "XL"] | None = None,
mode: Literal["answer", "auto", "investigate", "research"] | None = None,
structured_output_schema: type[BaseModel] | dict[str, Any] | str | None = None,
from_date: date | None = None,
to_date: date | None = None,
from_date: date | str | None = None,
to_date: date | str | None = None,
exclude_domains: list[str] | None = None,
include_domains: list[str] | None = None,
timeout: float | None = None,
Expand All @@ -394,10 +400,12 @@ async def async_research(
structured_output_schema: If output_type is "structured", specify the output schema.
Supported formats are a pydantic.BaseModel, a Python dictionary containing a valid
object JSON schema, or a string representing a valid object JSON schema.
from_date: The date from which the research sources should be considered. If None,
sources will not be filtered by a start date.
to_date: The date until which the research sources should be considered. If None,
sources will not be filtered by an end date.
from_date: The date from which the research sources should be considered. Accepts a
`datetime.date`, `YYYY-MM-DD`, or full ISO datetime string. If None, sources will
not be filtered by a start date.
to_date: The date until which the research sources should be considered. Accepts a
`datetime.date`, `YYYY-MM-DD`, or full ISO datetime string. If None, sources will
not be filtered by an end date.
exclude_domains: Domains to exclude from the research sources.
include_domains: Domains to restrict the research sources to.
timeout: The timeout for the HTTP request, in seconds. If None, the request will have
Expand Down
67 changes: 67 additions & 0 deletions tests/unit/client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,24 @@ class Company(BaseModel):
b'{"results": []}',
linkup.SearchResults(results=[]),
),
(
{
"query": "query",
"depth": "standard",
"output_type": "searchResults",
"from_date": "2026-05-01T08:15:30.000Z",
"to_date": "2026-05-31T23:59:59.000Z",
},
{
"q": "query",
"depth": "standard",
"outputType": "searchResults",
"fromDate": "2026-05-01T08:15:30.000Z",
"toDate": "2026-05-31T23:59:59.000Z",
},
b'{"results": []}',
linkup.SearchResults(results=[]),
),
(
{
"query": "query with timeout",
Expand Down Expand Up @@ -688,6 +706,55 @@ async def test_async_research(mocker: MockerFixture, client: linkup.Client) -> N
assert research_response.input.structured_output_schema == {"type": "object"}


def test_research_with_iso_datetime_string_dates(
mocker: MockerFixture, client: linkup.Client
) -> None:
request_mock = mocker.patch(
"httpx.Client.request",
return_value=Response(
status_code=200,
content=b"""
{
"createdAt": "2026-05-18T00:00:00.000Z",
"error": null,
"id": "f93f33c8-2688-4bd0-ab11-47c8ff89f7b7",
"input": {
"fromDate": "2026-05-01",
"outputType": "sourcedAnswer",
"q": "query",
"toDate": "2026-05-31"
},
"output": null,
"status": "pending",
"type": "research",
"updatedAt": "2026-05-18T00:00:00.000Z"
}
""",
),
)

research_response = client.research(
query="query",
output_type="sourcedAnswer",
from_date="2026-05-01T08:15:30.000Z",
to_date="2026-05-31T23:59:59.000Z",
)

request_mock.assert_called_once_with(
method="POST",
url="/research",
json={
"q": "query",
"outputType": "sourcedAnswer",
"fromDate": "2026-05-01T08:15:30.000Z",
"toDate": "2026-05-31T23:59:59.000Z",
},
timeout=None,
)
assert research_response.input.from_date == "2026-05-01"
assert research_response.input.to_date == "2026-05-31"


test_fetch_parameters = [
(
{"url": "https://example.com"},
Expand Down
Loading