ThrustLab

Pagination

Every list endpoint returns a CursorPager. You can either iterate the pager (which lazily fetches subsequent pages), or read .data and .has_more directly for "show more"-style UI.

Auto-iterate every page

The simplest mode — the pager fetches the next page transparently when you exhaust the current one.

from thrustlab import Client

client = Client()
for project in client.projects.list():
    print(project["id"], project["name"])

This is safe even on huge result sets: the pager only fetches one page at a time. Break out early to stop:

recent = []
for sim in client.simulations.list():
    recent.append(sim)
    if len(recent) >= 100:
        break

Manual cursor (UI flow)

For "load more" buttons or infinite scroll, fetch one page and check has_more:

page = client.projects.list(limit=20)
items = page.data       # list[dict] — first 20
print(f"has_more: {page.has_more}")

if page.has_more:
    last_id = items[-1]["id"]
    page2 = client.projects.list(limit=20, cursor=last_id)
    items.extend(page2.data)

cursor= is the SDK-wide pagination kwarg on every list() method — the opaque next_cursor token from the previous response (falls back to the last item's id if the response omits next_cursor). The server returns the next page after that cursor.

All list parameters

ParameterDefaultRangeNotes
limit201..100 (up to 1..1000 on simulations, sweeps, and dynamic_simulations lists)Page size — items returned per server hit
cursorNoneopaque tokenPagination cursor — next_cursor from the previous page

Resource-specific filters are layered on top — see each resource's own methods table (e.g. components.list(type=...), simulations.list(project_id=...)).

What CursorPager exposes

page = client.projects.list(limit=20)

page.data          # list[dict] — items on the current page
page.has_more      # bool — True if there are more pages
for item in page:  # iter walks every page until exhausted
    ...

You can iterate the pager once. To re-iterate, call list(...) again.

One-hit-or-raise: .one() and .first()

For "I expect exactly one match" lookups, .one() and .first() skip the iteration dance. Both are bounded — they only inspect the first page, they never exhaust the pager:

# Raises if zero or more than one match.
motor = client.components.list(type="motor", search="T-Motor F80").one()

# Returns the first item, or None if there are none — never raises.
motor = client.components.list(type="motor", search="T-Motor F80").first()

.one() raises NotFoundError for zero matches, and AmbiguousComponentError (from thrustlab.exceptions, a direct ThrustlabError subclass — not an APIError) if more than one item matched, including the case where exactly one item is on the first page but the pager still has more pages. The exception carries candidates (aliased matches) so you can disambiguate:

from thrustlab.exceptions import AmbiguousComponentError, NotFoundError

try:
    motor = client.components.list(type="motor", search="F80").one()
except AmbiguousComponentError as exc:
    motor = next(c for c in exc.candidates if c["name"] == "T-Motor F80 Pro")
except NotFoundError:
    motor = None

client.components.find(...) is a convenience wrapper over .list(**filters).one() — see Components.

Order

Server-default ordering is by created-at descending (newest first), and within a single created-at the tiebreaker is the resource id. Cursors preserve this order across pages.

Recipe: collect every result into one list

all_sims = list(client.simulations.list(project_id="proj_xxx"))
print(f"total: {len(all_sims)}")

For a million-row table this loads everything into memory — for huge collections, prefer streaming with for ... in.

Recipe: server-side filter then paginate

list() accepts resource-specific filters as keyword args. Apply them at the server, not in your loop, to avoid round-trips on rows you don't care about.

# All KDE motors with Kv between 400 and 900, lazily paginated
for motor in client.components.list(
    type="motor", brand_in=["KDE"], kv_gte=400, kv_lte=900
):
    print(motor["id"], motor["spec_json"]["kv_rpm_per_v"])

Recipe: parallel pagination across many resources

Fetch pages from independent resources concurrently using concurrent.futures:

from concurrent.futures import ThreadPoolExecutor
from thrustlab import Client

client = Client()

def first_page(resource_name):
    return list(getattr(client, resource_name).list(limit=20).data)

with ThreadPoolExecutor(max_workers=4) as ex:
    futures = {
        name: ex.submit(first_page, name)
        for name in ("projects", "simulations", "sweeps", "webhook_endpoints")
    }
    for name, fut in futures.items():
        print(name, len(fut.result()))

The SDK's underlying httpx.Client is thread-safe, so concurrent calls through one Client instance are fine.

See also

We use cookies and analytics tools to understand how you use ThrustLab and improve the experience. Privacy Policy

Python SDK — Pagination · ThrustLab API