From fdfe6ad4293837c936bd6a46d472f05604ccacc9 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Fri, 17 Apr 2026 10:46:54 +0200 Subject: [PATCH 1/2] refactor: accept snake_case values for actors().list(sort_by=...) Change the sort_by Literal to pythonic snake_case values ('created_at', 'last_run_started_at') and translate them to the API's camelCase form ('createdAt', 'stats.lastRunStartedAt') inside the client. --- .../_resource_clients/actor_collection.py | 15 +++++++++++---- tests/integration/test_actor.py | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/apify_client/_resource_clients/actor_collection.py b/src/apify_client/_resource_clients/actor_collection.py index f9e6ac16..21f8b19b 100644 --- a/src/apify_client/_resource_clients/actor_collection.py +++ b/src/apify_client/_resource_clients/actor_collection.py @@ -21,6 +21,11 @@ from apify_client._types import Timeout +_SORT_BY_TO_API: dict[str, str] = { + 'created_at': 'createdAt', + 'last_run_started_at': 'stats.lastRunStartedAt', +} + @docs_group('Resource clients') class ActorCollectionClient(ResourceClient): @@ -48,7 +53,7 @@ def list( limit: int | None = None, offset: int | None = None, desc: bool | None = None, - sort_by: Literal['createdAt', 'stats.lastRunStartedAt'] | None = 'createdAt', + sort_by: Literal['created_at', 'last_run_started_at'] | None = 'created_at', timeout: Timeout = 'medium', ) -> ListOfActors: """List the Actors the user has created or used. @@ -66,7 +71,8 @@ def list( Returns: The list of available Actors matching the specified filters. """ - result = self._list(timeout=timeout, my=my, limit=limit, offset=offset, desc=desc, sortBy=sort_by) + api_sort_by = _SORT_BY_TO_API[sort_by] if sort_by is not None else None + result = self._list(timeout=timeout, my=my, limit=limit, offset=offset, desc=desc, sortBy=api_sort_by) return ListOfActorsResponse.model_validate(result).data def create( @@ -193,7 +199,7 @@ async def list( limit: int | None = None, offset: int | None = None, desc: bool | None = None, - sort_by: Literal['createdAt', 'stats.lastRunStartedAt'] | None = 'createdAt', + sort_by: Literal['created_at', 'last_run_started_at'] | None = 'created_at', timeout: Timeout = 'medium', ) -> ListOfActors: """List the Actors the user has created or used. @@ -211,7 +217,8 @@ async def list( Returns: The list of available Actors matching the specified filters. """ - result = await self._list(timeout=timeout, my=my, limit=limit, offset=offset, desc=desc, sortBy=sort_by) + api_sort_by = _SORT_BY_TO_API[sort_by] if sort_by is not None else None + result = await self._list(timeout=timeout, my=my, limit=limit, offset=offset, desc=desc, sortBy=api_sort_by) return ListOfActorsResponse.model_validate(result).data async def create( diff --git a/tests/integration/test_actor.py b/tests/integration/test_actor.py index 9f12b0ed..480e4477 100644 --- a/tests/integration/test_actor.py +++ b/tests/integration/test_actor.py @@ -60,7 +60,7 @@ async def test_list_actors_pagination(client: ApifyClient | ApifyClientAsync) -> async def test_list_actors_sorting(client: ApifyClient | ApifyClientAsync) -> None: """Test listing Actors with sorting.""" - result = await maybe_await(client.actors().list(limit=10, desc=True, sort_by='createdAt')) + result = await maybe_await(client.actors().list(limit=10, desc=True, sort_by='created_at')) actors_page = cast('ListOfActors', result) assert actors_page is not None From b19b5003b86102c0c2320da41b2b50afa1a7f4cf Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Fri, 17 Apr 2026 14:17:38 +0200 Subject: [PATCH 2/2] docs: document snake_case sort_by change in v3 upgrading guide Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/04_upgrading/upgrading_to_v3.mdx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/04_upgrading/upgrading_to_v3.mdx b/docs/04_upgrading/upgrading_to_v3.mdx index bba41288..4bea7801 100644 --- a/docs/04_upgrading/upgrading_to_v3.mdx +++ b/docs/04_upgrading/upgrading_to_v3.mdx @@ -185,3 +185,23 @@ On retries, the timeout doubles with each attempt (exponential backoff) up to `t The default timeout tier assigned to each method on non-storage resource clients has been revised to better match the expected latency of the underlying API endpoint. For example, a simple `get()` call now defaults to `short` (5 s), while `start()` defaults to `medium` (30 s) and `call()` defaults to `no_timeout`. If your code relied on the previous global timeout behavior, review the timeout tier on the methods you use and adjust via the `timeout` parameter or by overriding tier defaults on the `ApifyClient` constructor (see [Tiered timeout system](#tiered-timeout-system) above). + +## Snake_case `sort_by` values on `actors().list()` + +The `sort_by` parameter of `ActorCollectionClient.list()` and `ActorCollectionClientAsync.list()` now accepts pythonic snake_case values instead of the raw camelCase values used by the API. + +Before (v2): + +```python +client.actors().list(sort_by='createdAt') +client.actors().list(sort_by='stats.lastRunStartedAt') +``` + +After (v3): + +```python +client.actors().list(sort_by='created_at') +client.actors().list(sort_by='last_run_started_at') +``` + +The default value also changed from `'createdAt'` to `'created_at'` (behavior is unchanged). The client translates the snake_case value to the form expected by the API internally.