---
title: "Search for milestones by a given filter"
method: POST
path: "/milestones/search"
tags: ["milestones"]
---

# Search for milestones by a given filter

`POST /milestones/search`

## Request

- Base URL: `https://api.usepylon.com`
- URL: `https://api.usepylon.com/milestones/search`
- Auth: HTTP bearer

Filterable fields and their allowed operators:

 Field | Allowed operators |
 --- | --- |
 `project_id` | `equals`, `in`, `not_in` |
 `account_id` | `equals`, `in`, `not_in`, `is_set`, `is_unset` |
 `due_date` | `time_is_after`, `time_is_before`, `time_range`, `is_set`, `is_unset` |
 `created_at` | `time_is_after`, `time_is_before`, `time_range` |
 `updated_at` | `time_is_after`, `time_is_before`, `time_range` |
 `started_at` | `time_is_after`, `time_is_before`, `time_range`, `is_set` |
 `completed_at` | `time_is_after`, `time_is_before`, `time_range`, `is_set` |

Timestamp values use RFC3339 format. Milestones belonging to an unlaunched project are not returned.

**Rate limit:** 120 requests per minute

## Request body

- SearchMilestonesRequest
  - `cursor` string: The cursor to use for pagination.
  - `filter` Filter
    - `field` string: The field for this filter. For allowed fields, see the documentation for the specific endpoint you are using. For non-compound filters (any operators other than "and", "or", or "not"), field must be set, along with either value or values, depending on the operator.
    - `operator` 'equals' | 'not_equals' | 'contains' | 'does_not_contain' | 'in' | 'not_in' | 'and' | 'or' | 'not' | 'time_is_after' | 'time_is_before' | 'time_range' | 'string_contains' | 'string_does_not_contain' | 'is_set' | 'is_unset' | 'greater_than' | 'less_than' | 'greater_than_or_equals' | 'less_than_or_equals' | 'includes_all_of' | 'starts_with' | 'ends_with' | 'time_is_in_last' | 'time_is_not_in_last' | 'time_is_in_next' | 'time_is_not_in_next', required: The operator for this filter.
    - `subfilters` Filter[]: Sub-filters for this filter. Valid only when operator is "and", "or", or "not"; "not" takes exactly one subfilter. The maximum allowed depth for a tree of filters is 5, counting the top-level filter.
    - `value` string: The value for this filter. Used by single-valued operators: "equals", "not_equals", "contains", "does_not_contain", "string_contains", "string_does_not_contain", "starts_with", "ends_with", "time_is_after", "time_is_before", "time_is_in_last", "time_is_not_in_last", "time_is_in_next", "time_is_not_in_next", "greater_than", "less_than", "greater_than_or_equals", and "less_than_or_equals".
    - `values` string[]: The values for this filter. Used by multi-valued operators: "in", "not_in", and "includes_all_of". For "time_range", set to [start, end].
  - `limit` integer: The number of milestones to fetch. Defaults to 100. Must be between 1 and 1000.

## Response `200`

- SearchMilestonesResponseBody
  - `data` Milestone[], required
    - `account` MiniAccount
      - `external_ids` ExternalID[], nullable: External IDs associated with the account.
        - `external_id` string: The external ID. Must be unique per object type (ex. account).
        - `label` string: The label of the external ID. Must be unique per object.
      - `id` string, required: The ID of the account.
    - `created_at` string, date-time, required
    - `due_date` string, date-time
    - `id` string, required
    - `name` string, required
    - `project` MiniProject
      - `id` string, required: The ID of the project.
    - `updated_at` string, date-time, required
  - `pagination` Pagination: Pagination information for list responses. Absent when there are no more results.
    - `cursor` string, required: The cursor for the next page of results.
    - `has_next_page` boolean, required: Indicates if there is a next page of results.
  - `request_id` string, required

## Other responses

- `400`: The request was invalid or could not be completed.
- `401`: The request could not be authenticated for this API.
- `403`: The authenticated caller or organization is not allowed to access this endpoint or operation.
- `429`: The request exceeded the applicable rate limit.
- `500`: An unexpected internal error occurred.

## Changes

- **2026-11-01** `8522e66913b4`: 5 info
  - added the new `not` enum value to the request property `filter/operator`
  - added the new `time_is_in_last` enum value to the request property `filter/operator`
  - added the new `time_is_in_next` enum value to the request property `filter/operator`
  - added the new `time_is_not_in_last` enum value to the request property `filter/operator`
  - …1 more
- **2026-10-11** `dfb481067e5a`: 1 breaking, 53 info
  - the response property `data/items/account/external_ids` became nullable for the status `200`
  - added the non-success response with the status `401`
  - added the non-success response with the status `429`
  - added the non-success response with the status `500`
  - …50 more
- **2026-09-17** `fcfd7a1bd7b4`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/usepylon/apis/pylon-api/changes/milestones/search/post.md)

---

[API](https://skmtc.dev/usepylon/apis/pylon-api.md) - [All operations](https://skmtc.dev/usepylon/apis/pylon-api/llms.txt) - [OpenAPI document](https://skmtc.dev/usepylon/apis/pylon-api/revisions/7d018a2a6265?raw)
