# List contacts API with GET /api/v1/contacts

> The Mujib list contacts API returns your customers newest first, page by page, with limit, cursor and since. Examples in curl, JavaScript and Python.

The list contacts API is `GET /api/v1/contacts`: it returns your business's customers from WhatsApp, Messenger and Instagram, plus the ones your team added, newest first, 50 to a page. It needs a key with the `contacts.read` scope.

## What does the contacts list return?

It returns one page of customers in `data`, from the most recently first seen to the oldest, with a `nextCursor` for the next page, or `null` on the last page. A key reads its own business's customers only.

## Which parameters does it take?

All parameters are optional, and any other parameter returns a 400:

| Parameter | Type | Default | Meaning |
| --- | --- | --- | --- |
| `limit` | integer | 50 | Customers per page, up to 100 |
| `cursor` | string | | The previous page's `nextCursor`. See [cursor pagination](https://docs.mujib.ai/en/developers/pagination/) |
| `since` | ISO instant | | Customers first seen at or after this instant |

`since` needs a date and a time, with `Z` or an offset, such as `2026-10-01T00:00:00Z`. A date alone is refused. If you write a `+` offset into the URL yourself, write it as `%2B`, since a `+` in a URL reads as a space.

## How do I send the request?

```bash
curl "https://api.mujib.ai/api/v1/contacts?limit=50&since=2026-10-01T00:00:00Z" \
  -H "Authorization: Bearer $MUJIB_API_KEY"
```

```js
const url = new URL('https://api.mujib.ai/api/v1/contacts');
url.searchParams.set('limit', '50');
url.searchParams.set('since', '2026-10-01T00:00:00Z');

const res = await fetch(url, {
  headers: { Authorization: `Bearer ${process.env.MUJIB_API_KEY}` },
});
const body = await res.json();
if (!res.ok) throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`);
console.log(body.data.length, body.nextCursor);
```

```python
import os
import requests

res = requests.get(
    "https://api.mujib.ai/api/v1/contacts",
    params={"limit": 50, "since": "2026-10-01T00:00:00Z"},
    headers={"Authorization": f"Bearer {os.environ['MUJIB_API_KEY']}"},
    timeout=30,
)
body = res.json()
if not res.ok:
    raise RuntimeError(f"{res.status_code} {body['error']['code']}: {body['error']['message']}")
print(len(body["data"]), body["nextCursor"])
```

To read every page to the end, use the function in [cursor pagination](https://docs.mujib.ai/en/developers/pagination/).

## What does the response look like?

A 200. The data below is made up:

```json
{
  "data": [
    {
      "id": "5d8f2a61-0c4e-4b7a-9f13-6e2b8c0d4a57",
      "name": "سارة",
      "phone": "962790000000",
      "channel": "whatsapp",
      "stage": "qualified",
      "tags": ["استشارة"],
      "createdAt": "2026-11-02T09:14:37.205Z",
      "lastActiveAt": "2026-11-03T16:40:02.918Z"
    },
    {
      "id": "9b1e3c74-2a5d-4f08-8e6b-1c7d0a3f5e92",
      "name": null,
      "phone": null,
      "channel": "instagram",
      "stage": "new",
      "tags": [],
      "createdAt": "2026-10-28T14:05:12.481Z",
      "lastActiveAt": "2026-10-28T14:07:55.030Z"
    }
  ],
  "nextCursor": "MjAyNi0xMC0yOFQxNDowNToxMi40ODFafDliMWUzYzc0LTJhNWQtNGYwOC04ZTZiLTFjN2QwYTNmNWU5Mg"
}
```

Every customer always carries these fields; a field with no value comes as `null`, never missing:

| Field | Type | Meaning |
| --- | --- | --- |
| `id` | string | The customer's stable id, for [get a contact](https://docs.mujib.ai/en/developers/api-contact/) |
| `name` | string or `null` | The customer's name, or `null` when it is not known |
| `phone` | string or `null` | The number as stored, often international digits without `+` such as `962790000000`, or `null` when it is not known |
| `channel` | string | Where the customer came from (see the next table) |
| `stage` | string | The customer's stage (see the table after it) |
| `tags` | array of strings | The customer's tags; an empty array when none |
| `createdAt` | UTC instant | When the customer was first seen |
| `lastActiveAt` | UTC instant | The customer's last activity |

`channel` values:

| Value | Meaning |
| --- | --- |
| `whatsapp` | Wrote to you on WhatsApp |
| `facebook` | Wrote to you on Messenger |
| `instagram` | Wrote to you on Instagram |
| `manual` | Added by your team in the dashboard |
| `import` | Imported from a file |

`stage` values, and what the dashboard calls them:

| Value | In the dashboard |
| --- | --- |
| `new` | **New** |
| `engaged` | **Engaged** |
| `qualified` | **Qualified** |
| `won` | **Won** |
| `lost` | **Lost** |

## What errors can it return?

| HTTP | `code` | Why |
| --- | --- | --- |
| 400 | `VALIDATION_ERROR` | An unknown parameter, a `limit` out of range, a `since` that is not a valid instant, or a cursor that cannot be read |
| 401 | `UNAUTHORIZED` | No key, or a key that is not valid |
| 403 | `FORBIDDEN` | The key lacks `contacts.read` |
| 429 | `RATE_LIMITED` | You went over a rate limit |
| 500 | `INTERNAL` | An unexpected server error |

The details, and what to do about each, are in [API errors and rate limits](https://docs.mujib.ai/en/developers/errors-limits/).

## Frequently asked questions

### Does the list include Messenger and Instagram customers?

Yes. It includes customers from WhatsApp, Messenger and Instagram, the ones your team added by hand and the ones imported from a file. The channel field says where each came from.

### Why is phone empty for some customers?

Because Mujib takes no phone number from Messenger and Instagram messages. A number appears when the customer gives it in the conversation or your team adds it.

### Can I search by name or phone number?

No. The list takes limit, cursor and since only, and any other parameter returns a 400.

### Do my team's notes about a customer come out?

No. Team notes, custom fields and what the agent remembers never leave through the API.

---

Source: https://docs.mujib.ai/en/developers/api-contacts/ · Last updated: 2026-11-04
