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
8 changes: 8 additions & 0 deletions src/apify_client/_http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging
import os
import sys
from datetime import datetime, timezone
from http import HTTPStatus
from importlib import metadata
from typing import TYPE_CHECKING, Any
Expand Down Expand Up @@ -76,6 +77,13 @@ def _parse_params(params: dict | None) -> dict | None:
# Our API needs lists passed as comma-separated strings
elif isinstance(value, list):
parsed_params[key] = ','.join(value)
elif isinstance(value, datetime):
utc_aware_dt = value.astimezone(timezone.utc)

# Convert to ISO 8601 string in Zulu format
zulu_date_str = utc_aware_dt.strftime('%Y-%m-%dT%H:%M:%SZ')

parsed_params[key] = zulu_date_str
elif value is not None:
parsed_params[key] = value

Expand Down
14 changes: 14 additions & 0 deletions src/apify_client/clients/resource_clients/run_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from apify_client.clients.base import ResourceCollectionClient, ResourceCollectionClientAsync

if TYPE_CHECKING:
from datetime import datetime

from apify_shared.consts import ActorJobStatus

from apify_client.clients.base.resource_collection_client import ListPage
Expand All @@ -25,6 +27,8 @@ def list(
offset: int | None = None,
desc: bool | None = None,
status: ActorJobStatus | list[ActorJobStatus] | None = None,
started_before: str | datetime | None = None,
started_after: str | datetime | None = None,
) -> ListPage[dict]:
"""List all Actor runs.

Expand All @@ -39,6 +43,8 @@ def list(
offset: What run to include as first when retrieving the list.
desc: Whether to sort the runs in descending order based on their start date.
status: Retrieve only runs with the provided statuses.
started_before: Only return runs started before this date (inclusive).
started_after: Only return runs started after this date (inclusive).

Returns:
The retrieved Actor runs.
Expand All @@ -53,6 +59,8 @@ def list(
offset=offset,
desc=desc,
status=status_param,
startedBefore=started_before,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what is the best way to handle camelCase translation here?
We need to send it as startedBefore/startedAfter to API

Copy link
Contributor

@vdusek vdusek Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do this for other parameters as well, so I believe it'll work 😄

(you can test it on a beta release, once this is merged)

startedAfter=started_after,
)


Expand All @@ -70,6 +78,8 @@ async def list(
offset: int | None = None,
desc: bool | None = None,
status: ActorJobStatus | list[ActorJobStatus] | None = None,
started_before: str | datetime | None = None,
started_after: str | datetime | None = None,
) -> ListPage[dict]:
"""List all Actor runs.

Expand All @@ -84,6 +94,8 @@ async def list(
offset: What run to include as first when retrieving the list.
desc: Whether to sort the runs in descending order based on their start date.
status: Retrieve only runs with the provided statuses.
started_before: Only return runs started before this date (inclusive).
started_after: Only return runs started after this date (inclusive).

Returns:
The retrieved Actor runs.
Expand All @@ -98,4 +110,6 @@ async def list(
offset=offset,
desc=desc,
status=status_param,
startedBefore=started_before,
startedAfter=started_after,
)
19 changes: 19 additions & 0 deletions tests/integration/test_run_collection.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from datetime import datetime, timezone
from typing import TYPE_CHECKING

import pytest
Expand Down Expand Up @@ -54,3 +55,21 @@ async def test_run_collection_list_multiple_statuses(self, apify_client: ApifyCl
assert all(run.get('status') == ActorJobStatus.SUCCEEDED for run in single_status_runs.items)

self.teadown_runs(apify_client)

# Here we test that date fields can be passed both as datetime objects and as ISO 8601 strings
async def test_run_collection_list_accept_date_range(self, apify_client: ApifyClient) -> None:
self.setup_runs(apify_client)

run_collection = apify_client.runs()

date_obj = datetime(2100, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
iso_date_str = date_obj.strftime('%Y-%m-%dT%H:%M:%SZ')

# Here we test that date fields can be passed both as datetime objects and as ISO 8601 strings
runs_in_range_date_format = run_collection.list(started_before=date_obj, started_after=date_obj)
runs_in_range_string_format = run_collection.list(started_before=iso_date_str, started_after=iso_date_str)

assert hasattr(runs_in_range_date_format, 'items')
assert hasattr(runs_in_range_string_format, 'items')

self.teadown_runs(apify_client)