> For the complete documentation index, see [llms.txt](https://docs.graspil.com/en/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.graspil.com/en/api/get-users.md).

# Getting user data

Returns a paginated list of resource users with extended information.

### Request

{% hint style="info" %}
Don't forget to add the authorization header for the request. More details in [this](/en/api/auth.md) section
{% endhint %}

<mark style="color:green;">`GET`</mark> / `POST` `/v1/get-users`

Both `GET` and `POST` are supported. For a POST request, the body is JSON.

#### Pagination parameters

| Parameter | Type | Default | Description                   |
| --------- | ---- | ------- | ----------------------------- |
| `limit`   | int  | 20      | Number of records (max. 500)  |
| `offset`  | int  | 0       | Page number (starting from 0) |

> `offset` is the page number, not an absolute offset. The actual offset = `offset × limit`.

#### Filters

For GET, they are passed with the `filter_` prefix (e.g. `filter_status=0`).\
For POST, they are passed in the `filters` object without the prefix.

| Filter       | Type   | Description                                     |
| ------------ | ------ | ----------------------------------------------- |
| `user_id`    | int    | Telegram user ID                                |
| `username`   | string | Partial match on username (without @)           |
| `search`     | string | Search by `first_name`, `last_name`, `username` |
| `status`     | int    | `0` — active, `1` — blocked the bot / left      |
| `is_premium` | int    | `0` — no Premium, `1` — has Premium             |
| `gender`     | int    | `0` — undefined, `1` — male, `2` — female       |

Examples

**GET:**

{% code overflow="wrap" %}

```
GET /v1/get-users?limit=10&offset=0&filter_status=0&filter_is_premium=1
```

{% endcode %}

**POST:**

{% code overflow="wrap" %}

```json
POST /v1/get-users
Api-Key: YOUR_API_KEY
Content-Type: application/json

{
  "limit": 10,
  "offset": 0,
  "filters": {
    "status": 0,
    "is_premium": 1
  }
}
```

{% endcode %}

<br>

### Response

{% code overflow="wrap" %}

```json
{
  "ok": true,
  "data": {
    "count": 1234,
    "rows": [
      {
        "user_id": 123456789,
        "is_bot": false,
        "first_name": "Ivan",
        "last_name": "Petrov",
        "username": "ivanpetrov",
        "language_code": "ru",
        "is_premium": 1,
        "gender": 1,
        "time_zone": 3,
        "timezone_offset": 10800,
        "date_create": 1700000000,
        "date_last_active": 1716000000,
        "user_status": 0,
        "country": "Russia",
        "city": "Moscow",
        "verified": null,
        "scam": null,
        "fake": null,
        "bio": null,
        "stargifts_count": null,
        "tg_version": null,
        "personal_channel_id": null,
        "birth_day": null,
        "birth_month": null,
        "birth_year": null,
        "devices_name": null,
        "viewport_height": null,
        "geo": {
          "countryCode": "RU",
          "countryName": "Russia",
          "cityName": "Moscow",
          "latitude": 55.75,
          "longitude": 37.62
        },
        "utm": {
          "first": { "source": "telegram", "campaign": "promo2024" },
          "last": { "source": "direct" },
          "weight": {}
        },
        "addition_fields": {
          "phone": "+7999...",
          "custom_field": "value"
        },
        "connection_data": {}
      }
    ]
  }
}
```

{% endcode %}

#### Response fields

| Field                 | Type         | Description                                          |
| --------------------- | ------------ | ---------------------------------------------------- |
| `user_id`             | int          | Telegram user ID                                     |
| `is_bot`              | bool\|null   | Whether the user is a bot                            |
| `first_name`          | string\|null | First name                                           |
| `last_name`           | string\|null | Last name                                            |
| `username`            | string\|null | Username (without @)                                 |
| `language_code`       | string\|null | Telegram interface language (`ru`, `en`, ...)        |
| `is_premium`          | int\|null    | Telegram Premium: `1` — yes, `0` — no                |
| `gender`              | int\|null    | Gender: `0` — undefined, `1` — male, `2` — female    |
| `time_zone`           | float\|null  | UTC offset in hours (e.g. `3` for UTC+3)             |
| `timezone_offset`     | int\|null    | UTC offset in seconds                                |
| `date_create`         | int\|null    | Unix timestamp of the first interaction with the bot |
| `date_last_active`    | int\|null    | Unix timestamp of the last activity                  |
| `user_status`         | int\|null    | Status: `0` — active, `1` — blocked the bot / left   |
| `country`             | string\|null | Country (from geo, determined by IP)                 |
| `city`                | string\|null | City (from geo, determined by IP)                    |
| `verified`            | bool\|null   | Telegram verification flag                           |
| `scam`                | bool\|null   | Scam account flag                                    |
| `fake`                | bool\|null   | Fake account flag                                    |
| `bio`                 | string\|null | Bio from the Telegram profile                        |
| `stargifts_count`     | int\|null    | Number of Star Gifts                                 |
| `tg_version`          | string\|null | Telegram client version                              |
| `personal_channel_id` | int\|null    | Personal channel ID (if set)                         |
| `birth_day`           | int\|null    | Birth day                                            |
| `birth_month`         | int\|null    | Birth month                                          |
| `birth_year`          | int\|null    | Birth year                                           |
| `devices_name`        | string\|null | Device name                                          |
| `viewport_height`     | int\|null    | Mini App viewport height in pixels                   |
| `geo`                 | object       | Full geolocation object (IP-based)                   |
| `utm`                 | object       | UTM parameters: `first`, `last`, `weight`            |
| `addition_fields`     | object       | Custom fields added through the bot                  |
| `connection_data`     | object       | Connection metadata                                  |

> All scalar fields are always present in the response. If there is no data, the value is `null`. Objects (`geo`, `utm`, `addition_fields`, `connection_data`) are returned as `{}` when there is no data.

***

### The `geo` object

The user's geolocation, if it was determined.

{% code overflow="wrap" %}

```json
"geo": {
  "ip": "98.116.0.1",
  "countryCode": "US",
  "countryName": "United States",
  "regionName": "New York",
  "region": "NY",
  "cityName": "New York",
  "timezone": "America/New_York",
  "timezone_offset": -14400,
  "lat": 40.7128,
  "lon": -74.0060,
  "zip": "10001"
}
```

{% endcode %}

| Field             | Type         | Description                                       |
| ----------------- | ------------ | ------------------------------------------------- |
| `ip`              | string\|null | User's IP address                                 |
| `countryCode`     | string\|null | ISO 3166-1 alpha-2 country code (e.g. `RU`, `US`) |
| `countryName`     | string\|null | Country name                                      |
| `regionName`      | string\|null | Full region/area name                             |
| `region`          | string\|null | Short region code                                 |
| `cityName`        | string\|null | City name                                         |
| `timezone`        | string\|null | IANA timezone (e.g. `America/New_York`)           |
| `timezone_offset` | int\|null    | UTC offset in seconds (e.g. `10800` for UTC+1)    |
| `lat`             | float\|null  | Latitude                                          |
| `lon`             | float\|null  | Longitude                                         |
| `zip`             | string\|null | Postal code                                       |

> The `countryName` and `cityName` fields are also duplicated at the root of the user object for convenience.

***

### The `utm` object

{% code overflow="wrap" %}

```json
"utm": {
  "first": {
    "source": "telegram",
    "campaign": "promo2024",
    "none": "raw_start_value"
  },
  "last": {
    "source": "google",
    "medium": "cpc"
  },
  "weight": {
    "source": "telegram"
  },
  "referral_id": 987654321,
  "invite_link_id": 42
}
```

{% endcode %}

| Field            | Type      | Description                                              |
| ---------------- | --------- | -------------------------------------------------------- |
| `first`          | object    | UTM parameters of the user's **first** visit             |
| `last`           | object    | UTM parameters of the **last** visit                     |
| `weight`         | object    | UTM parameters of the last significant visit             |
| `referral_id`    | int\|null | Telegram user ID of the user who invited them (referrer) |
| `invite_link_id` | int\|null | ID of the invite link the user came through              |

The keys inside `first` / `last` / `weight` are the names of the UTM parameters configured in the bot (e.g. `source`, `medium`, `campaign`, `content`, `term`).

***

### The `addition_fields` object

Arbitrary custom fields that the bot records for a specific user — for example, via the automation action "Write field" or via the `send-user-field` API. The set of keys and value types is entirely determined by your bot's logic.

{% code overflow="wrap" %}

```json
"addition_fields": {
  "phone": "+79991234567",
  "email": "user@example.com",
  "score": 42,
  "subscribed": true,
  "referral_code": "ABC123"
}
```

{% endcode %}

The value can be a string, number, boolean, or `null`. If the user has no custom fields, `{}` is returned.

***

### The `connection_data` object

Data from **connected bots** (Connected Resources). If your bot is configured to receive data from another bot (for example, the user has already interacted with the main bot, and a child bot is used here), this object will contain UTM and custom fields from the connected bot.

The object's keys are string identifiers of the connected resources (`resource_key`). Each nested object can contain:

{% code overflow="wrap" %}

```json
"connection_data": {
  "main_bot": {
    "utm": {
      "first": { "source": "telegram" },
      "last": { "source": "telegram" },
      "weight": {}
    },
    "addition_fields": {
      "phone": "+79991234567"
    }
  }
}
```

{% endcode %}

| Field             | Description                                                             |
| ----------------- | ----------------------------------------------------------------------- |
| `utm`             | The user's UTM data from the connected bot (if UTM transfer is enabled) |
| `addition_fields` | Custom fields from the connected bot (if parameter transfer is enabled) |

If there are no connected bots, or data transfer is not configured, `{}` is returned.

### Error codes

| Code  | Description                                        |
| ----- | -------------------------------------------------- |
| `401` | API key not provided or invalid                    |
| `403` | Method unavailable on your plan (Premium required) |
| `500` | Internal server error                              |

**Example error:**

{% code overflow="wrap" %}

```json
{
  "ok": false,
  "error": { "errors": "Premium tariff required" },
  "error_code": 403
}
```

{% endcode %}
