# Get Users with Filters

Search the traders in your community by profile, join date, balance, role or custom field, and get back only the fields you ask for, 200 at a time.

- Endpoint: `POST https://api.returning.ai/v1/users/filter`
- Section: Users and data / Users
- Authentication: `Authorization: Bearer <API_KEY>` (Community API key)
- Permission: `getUserData` (Shown in the dashboard as "Get User Data")
- Retries: Read-only; exact retries are safe
- Guide: hand-written
- Verified: live, 2026-09-27
- Last updated: 26 Sep 2026
- Web page: https://docs.returning.ai/api-reference/users/get-users-with-filters

## When to use this

- Check whether a trader with a broker customer ID already exists before you call Create User.
- Sync or reconcile your trader list, for example everyone who joined this month.
- Build a segment, such as traders with a role or with more than a set number of coins.

**Instead:** Use [Get User Data](https://docs.returning.ai/api-reference/users/get-user-data.md) instead when you already know one trader's email or platform user ID.

## Authentication

- Header: `Authorization: Bearer <API_KEY>`
- Permission: `getUserData`

Every search needs `getUserData` and only ever returns traders in the API key's community. Keep the key on your server.

## Request

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Authorization` | `string` | Yes | Community API key with `getUserData`. (`Bearer <API_KEY>`) |
| `Content-Type` | `string` | Yes | Request body format. (`application/json`) |

### Body

Send both `fields` and `filter`. `filter: {}` returns every trader in your community.

Supported filter keys, all of which must match:

- `email`, `first_name`, `last_name`, `username`, `country`, `phone`: exact value, not case-sensitive.
- Any custom field key, such as `customerid`: exact value, or an operator object on number and date fields. You can also group custom fields under `customFields`, for exact values only.
- `join_date`, `xp`, `coins`: an operator object with `gt`, `gte`, `lt`, `lte` or `eq`, for example `{"xp": {"gte": 1000}}`.
- `roles`, `tags`, `badges`: `{"inc": [...]}` for traders who have every listed name, `{"exc": [...]}` for traders who have none of them. Names are not case-sensitive.
- `language`: `{"eq": "en"}`.

There is no `page` parameter. To walk the results, send each response's `nextCursor` back as `cursor`.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `fields` | `string[]` | Yes | The fields to return for each trader. Profile: `id`, `_id`, `email`, `username`, `first_name`, `last_name`, `country`, `phone`, `join_date`. Activity: `xp`, `coins`, `language`, `roles`, `tags`, `badges`. You can also name any custom field key. (At least one) |
| `filter` | `object` | Yes | Conditions every returned trader must meet. Send `{}` to list everyone. (Object; `{}` allowed) |
| `filter.customerid` | `string \| number \| object` | No | Any custom field key, such as your broker customer ID. Matches the exact value, or use `gt`, `gte`, `lt`, `lte` or `eq` on number and date fields. |
| `filter.email` | `string` | No | Exact email, not case-sensitive. |
| `filter.first_name` | `string` | No | Exact first name, not case-sensitive. `last_name`, `username`, `country` and `phone` work the same way. |
| `filter.join_date` | `object` | No | When the trader joined your community. Use `gt`, `gte`, `lt`, `lte` or `eq`; combine two for a range. |
| `filter.join_date.gte` | `string` | No | On or after this date. A date-only value (`YYYY-MM-DD`) compares whole days. |
| `filter.join_date.lte` | `string` | No | On or before this date. A date-only value includes that whole day. |
| `cursor` | `string` | No | The `nextCursor` from the previous page. Omit it for the first page. (Send exactly as received) |

### Example request

```bash
curl --request POST \
  --url https://api.returning.ai/v1/users/filter \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
    "fields": [
      "id",
      "email",
      "username",
      "join_date",
      "customerid"
    ],
    "filter": {
      "customerid": "<brokerCustomerId>"
    }
  }'
```

## Response

A `200` returns up to 200 traders in `data.data`. No match is still a `200`, with an empty list and `total: 0`. Branch on the HTTP status, never on `message`.

**Note:** Results include the community owner and traders removed with Manage User Account, so this list is not proof that a trader is active. Use Get User Data to confirm one trader.

### Response fields

| Field | Type | Presence | Description |
| --- | --- | --- | --- |
| `status` | `string` | always | Result of the request. (`success`) |
| `data` | `object` | always | The page of results. |
| `data.data` | `object[]` | always | The matching traders, each with only the fields you asked for, in a fixed order. |
| `data.data.id` | `string` | when requested | Platform user ID. Store it as a string and never do arithmetic on it. |
| `data.data.email` | `string` | when requested | The trader's email. |
| `data.data.username` | `string` | when requested | The trader's username. |
| `data.data.first_name` | `string` | when requested | The trader's first name. |
| `data.data.last_name` | `string` | when requested | The trader's last name. |
| `data.data.join_date` | `string` | when requested | When the trader joined your community, or `null` if unknown. |
| `data.data.customerid` | `string` | when requested | A requested custom field, keyed by its field key. Holds the field's default value, or `null`, when the trader has no value. |
| `data.pagination` | `object` | always | Where this page sits in the full result. |
| `data.pagination.total` | `integer` | always | Number of matching traders across all pages. |
| `data.pagination.currentPage` | `integer` | always | This page's number, starting at 1. |
| `data.pagination.totalPages` | `integer` | always | Number of pages. (200 traders per page) |
| `data.pagination.hasNextPage` | `boolean` | always | `true` when another page follows. Send `nextCursor` to get it. |
| `data.pagination.hasPrevPage` | `boolean` | always | `true` on every page after the first. |
| `data.nextCursor` | `string` | when hasnextpage | Send this as `cursor` to get the next page. |

### Example response (200)

```json
{
  "status": "success",
  "data": {
    "pagination": {
      "total": 1,
      "currentPage": 1,
      "totalPages": 1,
      "hasNextPage": false,
      "hasPrevPage": false
    },
    "data": [
      {
        "id": "3247779",
        "email": "trader@example.com",
        "username": "sample_trader",
        "join_date": "2026-09-26T08:30:00.000Z",
        "customerid": "<brokerCustomerId>"
      }
    ]
  }
}
```

## Errors

Errors from the API key check, such as 401 and 403, carry the code in `meta.code`. A `400` has no code; its `detail` names each invalid field. A `500` has no code.

### Fix the request

| Status | Code | What to do |
| --- | --- | --- |
| 400 | - | `fields` or `filter` is missing, `fields` is empty, or a filter uses an unsupported operator. `detail` names the field. Send both keys; `filter` may be `{}`. |
| 401 | `AUTHENTICATION_REQUIRED` | The key is missing, invalid or expired. Send `Authorization: Bearer <API_KEY>` with a current key. |
| 403 | `API_KEY_PERMISSION_DENIED` | The key is valid but lacks `getUserData`. Add the permission in Settings > Integration > API Keys. |

### Fix the data

| Status | Code | What to do |
| --- | --- | --- |
| 404 | `COMMUNITY_NOT_FOUND` | The key's community no longer exists. Use a key from an active community. |

### Retry with backoff

| Status | Code | What to do |
| --- | --- | --- |
| 500 | - | An unexpected error, or a `cursor` that was not returned to you. Send `nextCursor` exactly as received, then retry with backoff. |
| 500 | `AUTHENTICATION_FAILED` | The key could not be checked. Retry the same request with backoff. |

**Retries:** This endpoint is read-only, so retrying the exact same request, with the same `cursor`, is safe after a network error or a 5xx. Use bounded exponential backoff. If traders join or leave while you page through, rows can move between pages, so de-duplicate by `id`. Over the [rate limit](https://docs.returning.ai/how-it-works.md#rate-limits), requests return `429`; wait for the window to reset, then retry.

## Next step

- [Get User Data](https://docs.returning.ai/api-reference/users/get-user-data.md): `POST /v1/users/info`. Read one trader's full profile, roles, coins and XP using the `id` from a row.
- [No match? Create User](https://docs.returning.ai/api-reference/users/create-user.md): `POST /v1/users`.
