---
title: "List Public Motors"
method: GET
path: "/v1/public/motors"
tags: ["v1", "public"]
---

# List Public Motors

`GET /v1/public/motors`

## Request

- Base URL: `https://thrustlab.com`
- URL: `https://thrustlab.com/v1/public/motors`
- Auth: none required

Unauthenticated curated-motor list with the full datasheet projection.

Paged with a KEYSET cursor, because the page cap is 200 and the catalog is
not: without ``starting_after`` the open database would end at row 200 and
the word "open" would be doing no work.

``sort_by=power_max`` is refused here although the authenticated catalog
accepts it: a motor's maximum power depends on the pack voltage, which a
reader browsing the catalog has not chosen. ``current_max`` is the
motor-limit facet that does not.

Motors also accept `curve_pack_cells[gte]` / `curve_pack_cells[lte]` to
filter by the pack cell count used for published curves, including both
limits.

Numeric fields are filterable with bracket syntax — `?kv[gte]=900&kv[lte]=1100`
— and the bounds are inclusive. Discrete fields take `[in]` with a
comma-separated list — `?brand[in]=T-Motor,Hobbywing`. A bracket filter naming a
field this type does not have is a 400 `unknown_filter` listing the fields it
does have, so a typo cannot quietly return the whole catalog.

Carries `license` because there is no bulk export: a consumer reading the open
database page by page has to learn the terms from the response, and this is the
only place left that can tell it.

## Query parameters

- `limit` integer
- `starting_after` string, nullable: Where the next page starts — the `next_starting_after` the previous page returned. Under the default ordering (`sort_by=name`, `sort_order=asc`) that is the last component's `id`, and a component `id` is accepted under any ordering. Under any other ordering it is an opaque cursor bound to that `sort_by`/`sort_order`, and replaying it against a different ordering is a 400 `invalid_cursor`. Omit for the first page.
- `search` string, nullable: Case-insensitive substring match on the component name.
- `q` string, nullable: Alias for `search`. If both are supplied, `search` wins. Length-capped identically.
- `brand` string[], nullable: Restrict to these brands. Repeat the parameter for several (`?brand=T-Motor&brand=Hobbywing`). `brand[in]=a,b` does the same.
- `sort_by` string: Sort key. `name` (the default) on every type, plus — motor: R (or resistance), iq_max (or current_max), kv, n (or poles), weight; battery: c_rating, energy_density, series_cells, total_capacity_mAh (or capacity), weight_g (or weight); propeller: diameter, inertia, maxRPM (or max_rpm), num_blades, pd_ratio, pitch, weight. Either spelling of a field works, here and in the bracket filters. `pd_ratio` is a propeller's pitch divided by its diameter; `energy_density` is a pack's NOMINAL specific energy in Wh/kg (capacity x 3.7 V per cell x series cells / mass), a label figure for ranking packs against each other, not a measurement.
- `sort_order` string: `asc` or `desc`.

## Response `200`

Successful Response

- PublicComponentList: A page of the open catalog.
  - `data` PublicComponent[], required
    - `curve_pack_cells` integer, nullable: The field gives the cell count of the stand-in battery pack used for a motor's published curves. It is null on propellers, batteries and motors with no published curves.
    - `id` string, required: Public component id (comp_<ksuid>).
    - `name` string, required
    - `slug` string, nullable: The row's URL segment — the one GET /v1/public/{type}/by-slug/{slug} resolves. Null only for a row minted before the column existed; a client should fall back to the id rather than derive a slug of its own, because the minting rule is the server's.
    - `source` string, nullable: Where the row came from. Published, not hidden: a dataset whose rows cannot be attributed is one a reader has to take on trust.
    - `spec_json` object, required: The datasheet. The same projection an authenticated caller reads. A propeller's station arrays are digitised blade geometry and are not part of it.
    - `type` 'motor' | 'propeller' | 'battery', required
    - `updated_at` string, nullable: When the row was last touched (ISO 8601).
  - `has_more` boolean, required: True when a further page exists; page with starting_after.
  - `license` string, required: The licence this data is published under. Stated on the response because there is no bulk export to carry it.
  - `next_starting_after` string, nullable: Pass as starting_after to fetch the next page. Null on the last page, so a client can loop until it disappears. Under the default ordering it is the last component's id; under any other ordering it is an opaque cursor bound to that sort.
  - `object` 'list'

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/thrustlab/apis/thrustlab-api.md) · [All operations](https://skmtc.dev/thrustlab/apis/thrustlab-api/llms.txt) · [OpenAPI document](https://skmtc.dev/thrustlab/apis/thrustlab-api/revisions/c19717e52529?raw)
