> ## Documentation Index
> Fetch the complete documentation index at: https://docs.freshtalent.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# List target members

> List a Target's members with status, people, stint, location, role, and enrichment filters.

Returns member rows for one Target. It is the read operation for the dashboard's Everyone, Monitored, Pending Approval, and Removed views.

## Request

<ParamField path="id" type="string" required>
  Target UUID.
</ParamField>

All query parameters are optional. The route does not framework-validate query strings; use the documented values and URL-encode them.

<ParamField query="status" type="string">
  `all` (default), `proposed`, `watching`, or `excluded`. Dashboard labels are Pending Approval, Monitored, and Removed.
</ParamField>

<ParamField query="q" type="string">
  Case-insensitive substring search across `full_name`, `headline`, and `current_company`. `keywords` is accepted as an alias when `q` is absent.
</ParamField>

<ParamField query="stint" type="string">
  `all` (default), `current`, or `alum`, based on `is_current`. `company_current=true` is an alias for `stint=current`; `company_left_min_months_ago` greater than zero is an alias for `stint=alum`.
</ParamField>

<ParamField query="location" type="string" placeholder="France;Germany">
  Semicolon-separated places. The shared parser keeps at most 20 distinct segments and truncates each segment to 120 characters. Matching is case-insensitive; commas remain part of a place such as `Paris, France`.
</ParamField>

<ParamField query="exclude_locations" type="string" placeholder="France;London">
  Same 20-segment/120-character parser. Excluded locations take priority over included locations. With no `location`, this excludes matching places everywhere.
</ParamField>

<ParamField query="company" type="string">
  Case-insensitive substring match against `current_company`.
</ParamField>

<ParamField query="roles" type="string" placeholder="software-engineer:12@company,t.exact.Senior%20Engineer.24">
  Comma-separated role tokens, maximum five parsed roles. A family token is `family[:minimum_months][@company]`; `@company` sets company scope. A title token is `t.exact.<title-or-titles-separated-by-semicolons>.<minimum_months>` or `t.contains...`. The parser clamps encoded minimum months to 0–600, but this Target-member route applies the parsed family/title patterns to member headlines; it does not apply the parsed minimum-month or scope values. This filter is independent of the stored discovery query.
</ParamField>

<ParamField query="exclude_recruiters" type="boolean">
  Pass the exact string `true` to exclude headlines matching recruiter, talent-acquisition, sourcer, staffing, or talent-partner terms. Other values behave as false.
</ParamField>

<ParamField query="depth" type="string">
  `all` (default), `stub` (not enriched), or `enriched` (`enriched_at` is non-null).
</ParamField>

<ParamField query="first_seen_after" type="string">
  Timestamp parsed by PostgreSQL as `timestamptz`; rows are included when `first_seen_at` is at or after it. Use an ISO-8601 timestamp.
</ParamField>

<ParamField query="limit" type="integer">
  Default `50`; clamped to a minimum of `1` and maximum of `500`.
</ParamField>

<ParamField query="offset" type="integer">
  Default `0`; negative values become `0`. This is offset pagination, not cursor pagination.
</ParamField>

## Response and pagination

`200 OK` returns `{ "data": Member[], "meta": { "count": integer } }`. `meta.count` is the total number of rows matching all filters, not the page length. Rows are ordered with `proposed` first, then `watching`, then `excluded`, and by `full_name` with nulls last.

There is no `has_more` or server-generated `next` link. The next page is `offset + limit` while that value is below `meta.count`; stop when it is equal to or greater than `meta.count`.

Each member has this schema:

| Field                                                                                                                             | Type                                     | Nullability and meaning                   |
| --------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | ----------------------------------------- |
| `id`                                                                                                                              | string                                   | Non-null member UUID.                     |
| `watch_id`                                                                                                                        | string                                   | Non-null Target UUID.                     |
| `linkedin_url`                                                                                                                    | string                                   | Non-null normalized LinkedIn profile URL. |
| `status`                                                                                                                          | `"watching" \| "excluded" \| "proposed"` | Non-null membership state.                |
| `full_name`, `headline`, `current_company`, `public_identifier`, `linkedin_id`, `location`, `profile_picture_url`, `profile_slug` | string                                   | Nullable profile/enrichment fields.       |
| `is_current`                                                                                                                      | boolean                                  | Nullable when current/alumni is unknown.  |
| `enriched_at`                                                                                                                     | string                                   | Nullable timestamp.                       |
| `first_seen_at`                                                                                                                   | string                                   | Non-null timestamp.                       |

<ResponseExample>
  ```json 200 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "data": [
      {
        "id": "8c5f6a6b-5cb7-4e3f-a1fd-9aabf2d2c6e0",
        "watch_id": "550e8400-e29b-41d4-a716-446655440000",
        "linkedin_url": "https://www.linkedin.com/in/derek-morrow",
        "status": "proposed",
        "full_name": "Derek Morrow",
        "headline": "Forward Deployed Engineer",
        "current_company": "Palantir",
        "is_current": false,
        "public_identifier": "derek-morrow",
        "profile_slug": "derek-morrow",
        "linkedin_id": "123456789",
        "location": "New York, United States",
        "profile_picture_url": null,
        "enriched_at": null,
        "first_seen_at": "2026-09-07T10:00:00.000Z"
      }
    ],
    "meta": { "count": 1 }
  }
  ```
</ResponseExample>

## Examples

<RequestExample>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl --fail-with-body --max-time 30 "https://api.freshtalent.ai/v1/targets/550e8400-e29b-41d4-a716-446655440000/members?status=proposed&limit=50&offset=0" \
    -H "Authorization: Bearer $FRESHTALENT_API_KEY"
  ```

  ```js JavaScript fetch theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const params = new URLSearchParams({ status: "proposed", limit: "50", offset: "0" });
  const response = await fetch(
    `https://api.freshtalent.ai/v1/targets/550e8400-e29b-41d4-a716-446655440000/members?${params}`,
    { signal: AbortSignal.timeout(30_000), headers: { Authorization: `Bearer ${process.env.FRESHTALENT_API_KEY}` } },
  );
  const body = await response.json();
  if (!response.ok) throw new Error(`${response.status}: ${JSON.stringify(body)}`);
  const next = body.meta.count > 50 ? new URL(response.url) : null;
  if (next) next.searchParams.set("offset", "50");
  console.log(body.data, next?.toString());
  ```

  ```python Python stdlib theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import json
  import os
  import urllib.parse
  import urllib.request

  query = urllib.parse.urlencode({"status": "proposed", "limit": 50, "offset": 0})
  request = urllib.request.Request(
      "https://api.freshtalent.ai/v1/targets/550e8400-e29b-41d4-a716-446655440000/members?" + query,
      headers={"Authorization": f"Bearer {os.environ['FRESHTALENT_API_KEY']}"},
  )
  with urllib.request.urlopen(request, timeout=30) as response:
      body = json.load(response)
  print(body["data"], body["meta"]["count"])
  ```
</RequestExample>

## Errors and recovery

* `401 unauthorized`, `402 payment_required`, and platform-key `400 invalid_request`: fix authentication, access, or organization header.
* `404 not_found` with `watch not found`: verify the Target ID and organization.
* `429`/`5xx`: retry the read with bounded backoff and honor `Retry-After`.
* An invalid timestamp or malformed numeric query can become a database/framework error rather than a typed validation response. Send ISO timestamps and decimal non-negative integers.

## Lifecycle

A row with `status: "watching"` is the membership state used by Monitoring. Listing does not enrich or change members. A `proposed` row can be moved with [Accept target members](/api-reference/targets/admit); status changes to `watching` queue enrichment, but do not guarantee immediate monitoring freshness.

## Next

* [Get target](/api-reference/targets/get) for aggregate counts and job progress.
* [Accept target members](/api-reference/targets/admit) to mutate selected rows or a filtered set.
