Skip to content

List modules

GET/api/v2/workspaces/{slug}/projects/{project_id}/modules/

List the modules in a project. Use it to build a delivery board, to find the module a sync run created, or to pull everything a person leads.

Results are paginated and, by default, ordered by sort_order — the order modules appear in the Plane UI.

Path Parameters

slug:requiredstring

The workspace slug. It appears in your Plane URLs — in https://app.plane.so/my-team/projects/, the slug is my-team.

project_id:requiredstring (uuid)

The project whose modules you want.

Query Parameters

Filters

status:optionalstring

Return only modules in this lifecycle position. One of backlog, planned, in-progress, paused, completed, or cancelled.

Use status__in with a comma-separated list to match several at once, for example ?status__in=planned,in-progress.

lead_id:optionalstring (uuid)

Return only modules led by this user. Match is exact on the module's lead_id.

external_id:optionalstring

Return only modules carrying this external identifier. Pair it with external_source to resolve a record you imported from another system.

external_source:optionalstring

Return only modules that came from this system, for example github or jira.

Search

search:optionalstring

Free-text match on the module name.

Ordering

order_by:optionalstring

Field to sort by. Prefix with - for descending.

  • sort_order , -sort_order
  • created_at , -created_at
  • id , -id

Defaults to sort_order.

Pagination

per_page:optionalinteger

Page size. Defaults to 50, maximum 200.

offset:optionalinteger

Number of rows to skip from the start of the result set. Maximum 10000 — go deeper with cursor pagination.

paginate:optionalstring

Set to cursor to opt into the COUNT-free keyset envelope, then follow next_cursor with ?cursor=.

count:optionalboolean

Set to false to skip the count query and omit total_count from the offset envelope. Defaults to true.

Cursor needs an explicit order_by

The default sort_order is not unique, so it can't back a stable keyset. A bare ?paginate=cursor returns 400 ordering_not_cursor_eligible. Pair it with a cursor-eligible ordering — ?paginate=cursor&order_by=created_at — or stay on offset.

Enum filters are validated

status and status__in are checked against the allowed values. A typo like ?status=in_progress is a clean 400 validation_error, not an empty result set.

Modules do not support ?expand=lead_id and member_ids are always returned as ids.

Scopes

projects.modules:read

Errors

StatusCodeCause
401unauthorizedMissing or invalid credentials.
403forbiddenYour role or token scope can't read modules in this project.
404resource_not_foundNo such workspace or project, or it's outside your tenant.
429rate_limitedThrottled. Wait for the interval in Retry-After and retry.
List modules
bash
curl -X GET \
  "https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/modules/?status=in-progress&per_page=50" \
  -H "X-Api-Key: $PLANE_API_KEY"
Response200
json
{
  "data": [
    {
      "id": "7c1f3d90-2a64-4e58-9b0d-3fa1c7e28b45",
      "name": "Billing revamp",
      "description": "Rework subscription billing end to end.",
      "status": "in-progress",
      "start_date": "2026-01-05",
      "target_date": "2026-02-27",
      "lead_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
      "member_ids": ["16c61a3a-512a-48ac-b0be-b6b46fe6f430", "9d3e1f27-8b4c-4a06-95f1-2c7ea45b0d18"],
      "sort_order": 65535.0,
      "logo_props": {},
      "external_id": null,
      "external_source": null,
      "archived_at": null,
      "created_at": "2026-01-14T09:22:41.478363Z",
      "created_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430"
    },
    {
      "id": "3ea77b18-5c9d-4f02-8a61-b0d4c9e5137a",
      "name": "Search relevance",
      "description": "",
      "status": "in-progress",
      "start_date": null,
      "target_date": null,
      "lead_id": null,
      "member_ids": [],
      "sort_order": 98302.5,
      "logo_props": {},
      "external_id": null,
      "external_source": null,
      "archived_at": null,
      "created_at": "2026-01-19T14:03:07.201884Z",
      "created_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430"
    }
  ],
  "next": null,
  "previous": null,
  "total_count": 2,
  "pagination": { "style": "offset" }
}
Response200
json
{
  "data": [
    {
      "id": "7c1f3d90-2a64-4e58-9b0d-3fa1c7e28b45",
      "name": "Billing revamp",
      "description": "Rework subscription billing end to end.",
      "status": "in-progress",
      "start_date": "2026-01-05",
      "target_date": "2026-02-27",
      "lead_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
      "member_ids": ["16c61a3a-512a-48ac-b0be-b6b46fe6f430", "9d3e1f27-8b4c-4a06-95f1-2c7ea45b0d18"],
      "sort_order": 65535.0,
      "logo_props": {},
      "external_id": null,
      "external_source": null,
      "archived_at": null,
      "created_at": "2026-01-14T09:22:41.478363Z",
      "created_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430"
    }
  ],
  "next_cursor": "b3A9MTcx",
  "has_more": true,
  "pagination": { "style": "cursor" }
}