# List your students

- Endpoint: `GET /v1/student`
- Production: `https://api.main-team.org/v1/student`
- Sandbox: `https://apisnd.main-team.org/v1/student`
- Operation: `listStudents` (Students)
- Authentication: `Authorization: Bearer <token>`, a short-lived token you sign with your API key and secret
- Permission: `student/read:$org:$ID`

## Description

Lists the students your account registered, with `country`, `city`, `school`, `grade`, `supervisor` and `partner` resolved into records. A student another account registered never appears, even one with the same name.

Twenty students to a page unless you set `limit`, which is at most 100; a larger one is read as 100, and a `page` or `limit` that is not a number is read as the default rather than refused. `pagination.total` counts every one of your students, and `pagination.totalPages` says when to stop.

The students come in no guaranteed order, so a student registered while you page through can move others between pages. To find one student, keep the `_id` `registerStudent` answered with and call `getStudent`.

**Find students by email address.** `email` takes one address, or up to 100 separated by commas, and lists only your students who have one of them, matched exactly and without regard to case. Use it to find the `_id` of a student you registered earlier, such as after `registerStudent` answered `409` for an address that is already one of your students’. An address none of your students has matches nothing, whoever else holds it: the filter never looks beyond your own students. `pagination.total` counts the matches.

## Parameters

| Name | In | Required | Type | Description |
| --- | --- | --- | --- | --- |
| `email` | query | no | string | Only the students with one of these email addresses, separated by commas: at most 100. Matched exactly, without regard to case. An address none of your students has matches nothing. Refused with `400` when it is empty, given twice, holds a value that is not an address, or lists more than 100. |
| `page` | query | no | number | Page number. Defaults to 1. |
| `limit` | query | no | number | Items per page. Defaults to 20, max 100. |

## Response

`200` Success: `message` is "Students fetched successfully.".

```json
{
  "success": true,
  "message": "Students fetched successfully.",
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 57,
    "totalPages": 3
  },
  "data": [
    {
      "_id": "6650a1b2c3d4e5f6a7b8c9d0",
      "mainId": "6650a1b2c3d4e5f6a7b8c9d0",
      "username": "XXB1045",
      "firstName": "Jane",
      "lastName": "Doe",
      "fullName": "Jane Doe",
      "email": "jane.doe@example.com",
      "emailConfirmed": false,
      "phone": "+1 555 0100",
      "birth": "14/05/2008",
      "sex": "f",
      "country": {
        "_id": "6650a1b2c3d4e5f6a7b8c9d1",
        "name": "UNITED STATES",
        "iso3": "USA",
        "iso2": "US",
        "tz": "America/New_York",
        "dialCode": "+1",
        "flag": "🇺🇸",
        "createdAt": "2026-09-01T09:30:00.000Z",
        "updatedAt": "2026-09-02T14:05:00.000Z"
      },
      "city": {
        "_id": "6650a1b2c3d4e5f6a7b8c9d2",
        "name": "SPRINGFIELD",
        "country": "6650a1b2c3d4e5f6a7b8c9d1",
        "stateCode": "IL",
        "createdAt": "2026-09-01T09:30:00.000Z",
        "updatedAt": "2026-09-02T14:05:00.000Z"
      },
      "school": {
        "_id": "6650a1b2c3d4e5f6a7b8c9d3",
        "name": "SPRINGFIELD HIGH SCHOOL",
        "country": "6650a1b2c3d4e5f6a7b8c9d1",
        "city": "6650a1b2c3d4e5f6a7b8c9d2",
        "createdAt": "2026-09-01T09:30:00.000Z",
        "updatedAt": "2026-09-02T14:05:00.000Z"
      },
      "grade": {
        "_id": "6650a1b2c3d4e5f6a7b8c9d4",
        "name": "10",
        "createdAt": "2026-09-01T09:30:00.000Z",
        "updatedAt": "2026-09-02T14:05:00.000Z"
      },
      "supervisor": {
        "_id": "6650a1b2c3d4e5f6a7b8c9d5",
        "mainId": "6650a1b2c3d4e5f6a7b8c9d5",
        "firstName": "John",
        "lastName": "Smith",
        "fullName": "John Smith",
        "username": "XXT1003"
      },
      "partner": {
        "_id": "6650a1b2c3d4e5f6a7b8c9d5",
        "mainId": "6650a1b2c3d4e5f6a7b8c9d5",
        "firstName": "John",
        "lastName": "Smith",
        "fullName": "John Smith",
        "username": "XXT1003"
      },
      "activatedPlatformsThisSeason": [
        "common"
      ],
      "createdAt": "2026-09-01T09:30:00.000Z",
      "updatedAt": "2026-09-02T14:05:00.000Z"
    }
  ]
}
```

## Errors

| Status | Code | When |
| --- | --- | --- |
| 400 | [`bad_request`](https://hub.main-team.org/api/errors#bad_request) | `email` is empty, is given twice, or holds a value that is not an email address. |
| 400 | [`bad_request`](https://hub.main-team.org/api/errors#bad_request) | `email` lists more than 100 addresses. |
| 401 | [`unauthorized`](https://hub.main-team.org/api/errors#unauthorized) | The token is missing or malformed, is not signed with your account’s `apiSecret`, breaks the `iat` and `exp` rules, has expired or been revoked, or its account is not active. All of these answer the same. |
| 403 | [`forbidden`](https://hub.main-team.org/api/errors#forbidden) | The token is valid, but no role on your account allows `student/read` on `mto`, the organization every operation without `:organizationId` acts on, or a role denies it. |
| 429 | [`too_many_requests`](https://hub.main-team.org/api/errors#too_many_requests) | Your account has made more than 100 requests to this operation in the current 60-second window. Wait the seconds in `Retry-After` before sending again. |
| 500 | [`internal_error`](https://hub.main-team.org/api/errors#internal_error) | Something failed on our side. Retry later, and quote `request_id` if it goes on. |

## Code samples

### curl

```bash
# $TOKEN: a short-lived token you minted with your API key and secret
curl -sS 'https://api.main-team.org/v1/student?email=ada.lovelace%40example.org%2Cgrace.hopper%40example.org&page=1&limit=20' \
  -H "Authorization: Bearer $TOKEN"
```

### Node.js

```js
const token = process.env.TOKEN; // a short-lived token you minted with your API key and secret

const res = await fetch('https://api.main-team.org/v1/student?email=ada.lovelace%40example.org%2Cgrace.hopper%40example.org&page=1&limit=20', {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});
const body = await res.json();
if (!res.ok) throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`);
console.log(body.data, body.pagination);
```

### PHP

```php
<?php
$token = getenv('TOKEN'); // a short-lived token you minted with your API key and secret

$ch = curl_init('https://api.main-team.org/v1/student?email=ada.lovelace%40example.org%2Cgrace.hopper%40example.org&page=1&limit=20');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $token,
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
if ($response === false) {
    throw new RuntimeException(curl_error($ch));
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$body = json_decode($response, true);
if ($status >= 400) {
    $error = $body['error'];
    throw new RuntimeException("$status {$error['code']}: {$error['message']}");
}
print_r($body['data']);
print_r($body['pagination']);
```
