# Querying data

> Querying data via the Nexpay API.

Most of our endpoints allow you to retrieve specific data through query requests using various parameters.

Our API also lets you select what properties to return (like an SQL `SELECT` command), limit the quantity, sort and skip results.

---

## Query parameters

| Parameter | Example | Description |
| --- | --- | --- |
| `filter` | `status[0]=due&status[1]=paid-partial` | Defines a query object to filter the data based on the specified criteria. |
| `skip` | `skip=10` | Specifies the number of documents to skip in the query. This parameter is useful for pagination, allowing you to navigate through large sets of data. |
| `limit` | `limit=10` | Defines the number of documents to include in the API response. It enables users to limit the quantity of data retrieved. |
| `sort` | `createdAt` | Let's you sort the response based on different properties. Ascending or descending order, etc. To sort on a descending order, a hyphen character before the property name should be added (e.g. `-createdAt`). |
| `population` | `0[path]=contact&0[select]=profile` | Allows the expansion of specific properties of the response by specifying which fields to expand. Read more on on [Expanding Responses](#expanding-responses). |

---

## Example query request

The following request and response payload is an example of a query made to the Payment Intents API.

**cURL**

```bash
curl 'https://api.nexpay.com.au/v2/payment-intents?sort=createdAt&limit=14&skip=0' \
  -H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret'
```

**JavaScript**

```javascript
try {
  const response = await fetch('https://api.nexpay.com.au/v2/payment-intents?sort=createdAt&limit=14&skip=0', {
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': 'nxp_ck_your-client-id:nxp_sk_your-secret',
    },
  });

  if (response.ok) {
    const { data } = await response.json();

    console.log(data);
  } else {
    throw new Error(`Request failed with status: ${response.status}`);
  }
} catch (error) {
  console.error(error);
}
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'X-API-Key' => 'nxp_ck_your-client-id:nxp_sk_your-secret',
])->get('https://api.nexpay.com.au/v2/payment-intents', [
    'sort' => 'createdAt',
    'limit' => 14,
    'skip' => 0,
]);

$data = $response->json('data');

print_r($data);
```

### Query response

List responses follow a standard envelope: a top-level `data` object that holds the resource-named array (`paymentIntents`, `payees`, `students`, etc.) plus pagination meta at the top level alongside `data`.

```json
{
  "data": {
    "paymentIntents": [
      {
        "id": "507f1f77bcf86cd799439011",
        "status": "completed",
        "useCase": "education_provider_tuition",
        "deliveryMode": "manual",
        "createdAt": "2026-06-03T07:20:03.433Z"
      }
    ]
  },
  "hasMore": false,
  "count": 1,
  "total": 1410
}
```

Notes:

- `data.<resource>` is the array. The resource key matches the URL segment (`payees`, `students`, and `payment-intents` becomes `paymentIntents`, etc.).
- `hasMore` / `count` / `total` are top-level. Some list endpoints omit `total` for performance.
- Resources that come from Mongo collections (students, employees, payment-intents detail, etc.) expose their identifier as `id` in the public API even though the underlying document is keyed by `_id`. The `projection` parameter accepts either name.

## Encoding query parameters

### Objects

You need to encode JavaScript objects by converting key-value pairs into a query string format.

```javascript
// this object
{ key1: 'value1', key2: 'value2' }

// becomes
'key1=value1&key2=value2'
```

### Arrays

Arrays are handled by encoding their indices or keys.

```javascript
// this array
[ 'value1', 'value2' ]

// becomes
'0=value1&1=value2'

// or using the brackets notation
'arr[]=value1&arr[]=value2'
```

### Nested objects and arrays

Nested objects or arrays are encoded using dot notation.

```javascript
// this object
{ key: { nestedKey: 'value' } }

// becomes
'key.nestedKey=value'
```

### Special characters and URI component

#### Encoding special characters

You need to encode special characters, to allow their inclusion in query strings without causing parsing issues.

For example, spaces are replaced with `%20`.

#### URI component encoding

Use URI component encoding for preserving the integrity of URL query parameters. This encoding helps represent characters that have special meanings in URLs by converting them to a valid format.

> **Note — NPM packages can help you**
>
> The NPM package `qs` can help you encode all query parameters automatically. It's the same package internally used by Nexpay to stringify and parse query parameters.
>
> [Go to the NPM package page](https://www.npmjs.com/package/qs)

## Expanding responses

Many objects allow you to request additional information as an expanded response by using the `population` parameter. This parameter is available on most API query requests, and applies to the response of that request only.

To expand responses, add an extra parameter to the URL called `population`, and using the same encoding as explained above, choose which properties you want to expand.

```javascript
// this object
[{ path: 'contact', select: 'profile email' }]

// becomes
'0[path]=contact&0[select]=profile email'
```

## Filtering results

You can filter results of your query. Nexpay is compatible with a subset of the MongoDB query syntax. [You can learn more about how to create MongoDB-compatible object queries here.](https://www.mongodb.com/docs/manual/tutorial/query-documents/)

All filter inputs are sanitised before execution. The following sections describe what is supported and what is not.

### Supported filter operators

These comparison and logical operators are available for use in filters:

| Category | Operators | Example |
| --- | --- | --- |
| Comparison | `$eq`, `$gt`, `$gte`, `$lt`, `$lte`, `$ne`, `$in`, `$nin` | `age[$gte]=18` |
| Logical | `$or`, `$and`, `$not`, `$nor` | `$or[0][status]=active&$or[1][status]=pending` |
| Element | `$exists`, `$type` | `email[$exists]=true` |
| Array | `$elemMatch`, `$size`, `$all` | `tags[$elemMatch][$eq]=important` |

### Disallowed operations

The following operations are explicitly **not supported** and will be silently stripped from your query:

* **`$regex` and `$options`** — Regular expression matching is not available through query filters.
* **`$where`** — Server-side JavaScript execution is not permitted.
* **`$expr`** — Aggregation expressions within queries are not supported.
* **`$lookup`, `$unionWith`, `$merge`, `$out`** — Aggregation pipeline stages and cross-collection operations are not supported.
* **`$function`, `$accumulator`** — Custom server-side functions are not permitted.
* **`$comment`** — Query comments are stripped.

> **Warning — Filtering on sensitive fields**
>
> Queries that attempt to filter on sensitive fields (such as `password`, `apiKey`, `secret`, `accessToken`, `refreshToken`, or `privateKey`) will have those fields removed from the filter. This applies at any nesting depth.

### Query limits

* The `limit` parameter accepts values between `0` and `1000`.
* The `sort` and `projection` parameters only accept space-separated field names (e.g. `name email createdAt`). Dot notation for nested fields (e.g. `profile.firstName`) and a leading hyphen for descending order (e.g. `-createdAt`) are supported. JSON objects, special characters, and expressions are not accepted.
* Filter nesting depth is limited. Deeply nested filter structures beyond a reasonable depth will be truncated.
