Search API pagination can skip items when modified_at ordering is not strict

Hello Asana Support Team,

We are facing an issue with the Search API where results are not always strictly ordered by modified_at, which breaks pagination logic and can cause items to be skipped.

We are using the following endpoint:

GET /workspaces/{workspace_gid}/tasks/search

with parameters like:

  • portfolios.any

  • modified_at.after

  • sort_by=modified_at

  • limit

  • opt_fields

  • is_subtask=false

This returns paginated results sorted by modified_at, and we use the modified_at.before value from the last item of each page to fetch the next page.

However, in some responses, items near the page boundary are not strictly sorted by modified_at. For example, two items that should be ordered by modified_at appear out of order, where the item with the later modified_at appears after the one with the earlier modified_at.

With pagination enabled, this causes a scenario where using modified_at.before for the next page can exclude items that should have appeared, resulting in missing items.

We understand that Asana recommends using created_at for stable pagination, but our use case requires sorting by modified_at for performance.

Could you please confirm:

  1. Is the Search API guaranteed to return results strictly ordered by modified_at when using sort_by=modified_at?

  2. If not, what is the recommended safe way to paginate when using modified_at?

  3. Is this a known issue or something that can be addressed at the API level?

Similar ticket in the past for the same issue: Question about search api + paging

Thanks!

I brought the attention of the engineering team to this thread, hopefully they can help!

Hi @TCK_User1 , thanks for flagging and welcome to the forum! We’ve been quite busy with case work recently, but I’ve noted your report and will review it shortly. Thanks for your patience!

@TCK_User1 thanks for your patience while I took some time to investigate this and review prior reports. I’ve been able to reproduce the behavior you’re seeing: when using sort_by=modified_at, the Task Search API returns results where some tasks appear out of order relative to the modified_at value returned in the JSON.

Also, to add to your observation, this isn’t limited to items near page boundaries, it can occur anywhere in the response.

The underlying reason is that there are effectively two “modified” concepts associated with a task: one which reflects the last time the object was updated in the database and a separate reflecting changes that are visible in the Asana UI (as described in the earlier post).

On your questions:

1) Is the Search API guaranteed to return results strictly ordered by modified_at when using sort_by=modified_at?

Not with respect to the modified_at value returned by the API.

Strict ordering across multiple paginated requests would be impossible to guarantee for a rapidly changing field like modified_at anyway, because tasks are modified while you’re paginating.

2) If not, what is the recommended safe way to paginate when using modified_at?

It depends on your integration goal, but two approaches come to my mind:

Option A: Add deliberate overlap, de-dupe by task GID
When paginating based on a moving modified_at cursor, fetch the next page with an intentional overlap. Then de-dupe by task GID on your side. That would protect from the “boundary drift”.

Option B: Use “Get multiple tasks” with modified_since, sort client-side if needed
If your goal is incremental sync (“give me everything modified since”), using the task surface endpoint seems more robust to me. You can reliably page through results using stable pagination controls (limit/offset are supported in this context), then sort the final set by consumer-side if needed.

It’s a bit more legwork, but it gives you more deterministic control and avoids the quirks of search indexing (including occasional temporary indexing delays).

  1. Is this a known issue or something that can be addressed at the API level?

In a sense it’s known behavior. The search backend is sorting, just not necessarily by the exact timestamp seen in the API payload. I agree this can be surprising and is not suitable for pagination logic.

My search service is sorting by created_at, I believe that’s another way to avoid the issue isn’t it?

@Bastien_Siebman yes, that’s definitely better approach for output consistency.

Since created_at never changes, sorting by it gives you a stable ordering. When using modified_at, the consuming side should generally be designed to tolerate inevitable item shifting.

The documentation mentions it (Search tasks in a workspace) by saying you should sort by creation time because otherwise there is no guarantee. Maybe it should be even clearer with an example about modified_at not working as expected?