> For the complete documentation index, see [llms.txt](https://developer.esw.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.esw.com/customs-catalog-api/pagination-patterns.md).

# Pagination Patterns

## Offset-Based Pagination

Used by 8 of 9 paginated endpoints. Clients supply `PageNumber` + `PageSize` on every request. Server returns a `PagedResult` envelope with `totalCount` and `pageCount...`

{% hint style="info" icon="input-numeric" %}
**PageNumber** and **PageSize** are **required** on all offset-paged endpoints. All other parameters (filters, sort) are optional. PageNumber is 1-indexed.
{% endhint %}

{% tabs %}
{% tab title="GET /api/v2/Classifications" %}
Returns a list of product classifications.

| Parameter      | Type      | Description                 | Required |
| -------------- | --------- | --------------------------- | -------- |
| `RegionCode`   | `string`  | Filter by region            | No       |
| `PageNumber`   | `int32`   | Page to display (1-indexed) | Yes      |
| `PageSize`     | `int32`   | Records per page            | Yes      |
| `OrderByField` | `string`  | Field name to sort by       | No       |
| `IsOrderByAsc` | `boolean` | Sort direction              | No       |
| {% endtab %}   |           |                             |          |

{% tab title="GET /api/v2/Vat" %}
Returns a list of VAT rates with multi-filter support.

| Parameter    | Type     | Description             | Required |
| ------------ | -------- | ----------------------- | -------- |
| `VatBandId`  | `string` | Filter by VAT band      | No       |
| `RegionCode` | `string` | Filter by region        | No       |
| `CountryIso` | `string` | ISO country code filter | No       |
| `PageNumber` | `int32`  | Page to display         | Yes      |
| `PageSize`   | `int32`  | Records per page        | Yes      |
| {% endtab %} |          |                         |          |

{% tab title="GET /api/v2/Classifications/ChapterClassifications" %}
Filtered classification lookup with pagination.

| Parameter     | Type     | Description       | Required |
| ------------- | -------- | ----------------- | -------- |
| `RegionCode`  | `string` | Filter by region  | No       |
| `ChapterCode` | `string` | HS chapter filter | No       |
| `SectionCode` | `string` | HS section filter | No       |
| `PageNumber`  | `int32`  | Page to display   | Yes      |
| `PageSize`    | `int32`  | Records per page  | Yes      |
| {% endtab %}  |          |                   |          |

{% tab title="GET /api/v2/Products/ProductsUi" %}
Product list for UI display with brand filtering.

| Parameter               | Type     | Description           | Required |
| ----------------------- | -------- | --------------------- | -------- |
| `BrandCode`             | `string` | Filter by brand       | No       |
| `DefaultClassification` | `string` | Classification filter | No       |
| `TenantCode`            | `string` | Tenant scope          | No       |
| `PageNumber`            | `int32`  | Page to display       | Yes      |
| `PageSize`              | `int32`  | Records per page      | Yes      |
| {% endtab %}            |          |                       |          |
| {% endtabs %}           |          |                       |          |

## Cursor / Continuation Token

Used by **`/api/v2/Products/Invalid`** and **`/api/v2/Products/Invalid/File`**. An opaque `ContinuationToken` supplements `PageNumber`/`PageSize` for blob-backed result sets.

{% hint style="info" icon="binary" %}
These endpoints are **hybrid**: they accept both **ContinuationToken** (cursor) and **PageNumber/PageSize** (offset). The token is used when iterating blob-backed storage; PageNumber/PageSize remain for display paging.
{% endhint %}

{% tabs %}
{% tab title="GET /api/v2/Products/Invalid" %}
Invalid products for UI display with hybrid pagination.

| Parameter           | Type     | Description                               | Required |
| ------------------- | -------- | ----------------------------------------- | -------- |
| `BrandCode`         | `string` | Brand scope filter                        | No       |
| `SearchType`        | `string` | Type of search to apply                   | No       |
| `ContinuationToken` | `string` | Opaque blob cursor from the previous page | Cursor   |
| `PageNumber`        | `int32`  | Display page number                       | Yes      |
| `PageSize`          | `int32`  | Records per page                          | Yes      |
| {% endtab %}        |          |                                           |          |

{% tab title="GET /api/v2/Products/Invalid/File" %}
Catalog error file with the same hybrid pagination strategy.

| Parameter           | Type     | Description                               | Required |
| ------------------- | -------- | ----------------------------------------- | -------- |
| `BrandCode`         | `string` | Brand scope filter                        | No       |
| `RequestType`       | `string` | Type of catalog request                   | No       |
| `ContinuationToken` | `string` | Opaque blob cursor from the previous page | Cursor   |
| `PageNumber`        | `int32`  | Display page number                       | Yes      |
| `PageSize`          | `int32`  | Records per page                          | Yes      |
| {% endtab %}        |          |                                           |          |
| {% endtabs %}       |          |                                           |          |

{% @code-walkthrough/html-renderer aspectRatio="2.5" htmlZ0="H4sIAAAAAAAAA7VZ3W4buRW-11OcHReRnehnRrIseaRRtnWyqNvESRMbi8VigXBmOBLXHHJKciwrQYBc9apAgSIo2qu-RVH0cfwC7SMU5PxoRj-xE290IXEODw\_P-c4PD0eTb568ODn\_4eVTmKuYThsT\_QMUsZlnYWZBQJGUnkXJbK4sPY1ROG1MYqwQBHMkJFaedXH-XXtkFWSGYuxZVwQvEi6UBQFnCjPlWQsSqrkX4isS4LZ5aAFhRBFE2zJAFHtOx9ZiFFEUT09SIbmA7yhfwGsSpxQpwtmkm802JpSwSxCYelYicMAZw4GyYC5w5FlzpRLpdrsRZ0p2ZpzPKEYJkZ2Ax1ax9nbWbiBl73GEYkKX3pPnj55zxl2iEG0tZnP1rd3q2\_bYbh2a74Ftjx09fpAveL1k2DWMmuHItsdD2x6PbPtBSGRC0dKTC5RYmQ1SLSmWc4wNzOZp2gDjlY5BH941AADabX\_mwl5kPuOcJFMRoQDvovf0hB31okEx4XMRYuHCXuiEg9AvyCgIMFMu7Nk2Gg37dbKWcoiOgmhYp\_dd2MODPu7jkh77mfDj4dA-KqgKX2vRjuOMeqWIOFU4dGHvyB\_2RnZBDXiIcztHEYqCgi5QSFLpgmMn15r2vkAoROKyDpDt24FjbwLk9J2B428ByAmcqDfaAKh31EP9oy0A4QGyNwEa-MMgQpsARdGRf-SvAxRF\_uBwuAYQHmEU2XcFyA4dxxnuBqgB8LAFD13XxxEX2AxRpLCAd-Dz67YkbwmbuZAZ3Pb59RhiJGaEuWCPIUFhaObtcSbM5-EyR9pHweVM8JSFLlwhsa-RP8g0CTjloqBqu3K6TrJ2lh4uNHWCNFsgEZNtiQXJ47bcU1sBzmFmCoASiEmii4Bb2RvsTk-2sh3NuLS7-xBuPn64-fgBvhcoSbAoHh92GwCdEMe8vchndlmUB0iufhEVTnINklMSFnYbeo2pdEXGkT3lHPwKi4jyhQtzEoaY3cG8XOi6lYUZuiznNpTojWrg7TStV1fb50rx-BYT8\_rlQkRxLh9RMmNtonAsXdBRj0U28XMqFYmW7fwYcEEmKMBtH6sFLkyfocToe38gzOGQI2GCTZK32AWnXwg3xAXWFdUFXZM\_26CqsmsbF1kG76A0t3nzt381x1VljpPrcT1DsjpxMK7Ik6m\_YUuZOE-egz6Kmi2IOeMG0PGGyU5hcm0rU08O6rpXcnqVe71a-GwBSI\_aIRE4yFwVcJrGWxy6lov5se7z63o6BoauC9DOZMyr3j2SsYyxr2DnFwdubjlFPqZfz90AFCuFRVuvN6Z37EMcr2sh-CLXYQsUdXN3ZohBTFdWF\_T3-hYRwbSoVprVBeeWGnUPjx9tePxIO9yu1YNfAmlzpuc6xIRlDa4-Nw1lwUXY9gVGly6Ynzai9JeIGl-xex5eVcV3gbfT0vvAV\_dIr9zLGOZCwskqpHaC5AxkjvCcKGyCG7vA-LbA8xVz5\_rw1b1PBS0x89F-bzBoOSOnNXRaHacoxauFHYElVqZpWnmkXr\_7B1vLen-XsE\_o0jtujfqto16rY48Oxusl9OLVM-CpSlJVL6GpoO2c\_vVK6GZCjWruu09EfCJLKGG4Pc\_PbaczXCVaQezfrVG8NaMYV1\_11K258vWSKVQ\_CStXvs6ctudcKtNPbIpb3X9W7AlS8xX7Xn946AycrZyXeAnrgvOLzHbRV4juWLCdX-Jkg3-lee3qdmdDS-41O4Pj0AmPtzHWzdwbHgd2FG1jvJt5Jfut1q2cfD7HMQbFZzOK6xmr9Ew7n9nMWcYZ\_vJM7dmfLKmf3fNuZv7hL5b5O5uWWi6vp-76tQ8glbrDwRQHagXf-3WoV5V3ZzFfr-XZJRbeNybd\_OXMpJu\_DNPt87TRmITkqnhfVr1bWvo9zsakXmtmACYyQaw2aTp\_a-cbMM2\_a2lxb7CmrxVOQM0FT2dzSNAMS1gQNTc3E8JSIwsUv8RM1kT6qVK8FFqFzQLOAkqCS8\_KCCaw9w9yOwp1SJgvOw04s6b\_--ef\_1HdYIPtmW58rekTnVkxD3FNm26mjsGwG5KrrWBqDxRgVuZWV4qVhpvTNNv-5OLVq6dn53Dy4uz89Ozi1-enL87g\_MXvn56V--4QIPiilL-VwTS8ljE4ozzJUs-aspRS2I-IkMq4CG4-fAQeE\_0kUIwVFge17Tf8s2oqKt5h-FplwaOdc\_Onv8IZvlbwEs1wFdBbBILpUipizXNV7n\_glSaty9wJ2KpBqaJxIag1vQ1kfSpXF53p5-qqVXRkg\_ynMZGBIImaNgLOpIIIXeJzE\_TgwY8a\_xY08fJ3ix--D-npz3wZ\_cHzmnXa2y00ewvNMbSfxg2KVV5zT8Nr8PQloBGlzFwgIU1CpPBJNRD2D0zxzzQ0KQkAXkXXH0tpP41LRh0wZ2kMXmWvR9ldKuRBGmOmOjOsnlKsh79Znob7zVr8NQ86uqydZK8qwMsKrtn-cf7rQvMuMdocN0q1fCQxePCmVpvy892aFq\_bKZ8RqUgg20EqFY9lgBSifNZGCelIxEKfX3ewnPNkwQUN9dv4vC68gUdl7Oafjb10d2BNuygh3ate96XgYRoo2T1lV4iS8DMESZxY08efseASL63py8wzPhb5yq1ivR2TV4ha01-9y937\_nO1fYDiZLxDdKnda\_IWf7FuvUGh07getS-RUOBlY6Ps4y\_V8aRySJkUuAeQRp0SRqOXC83mHfLkQtDmQYcwhsVvz58\_Ay8L7kcra-8gRFeq9UyrQ6QdAiuHmwTbAAAiwWNIBL4iPJW6NiecSVzYkwlxzNozvmU5Z5DlsMB\_TLFUb8aN95WqVD0yTF9bKV\_PkZp3YsL2a3WmValPHYrZTM2hDfrqvLXC6d6p3K12kqztZn9CwEpCrQGpVE\_z\_6UunqVXikHumlXIEmlaDi9r7E3YPCNSdXSDhAiT-03d7DdNI7rGku2eM7Tgm0zUpzjNtajZggrnzrgpm6e1" htmlZ1="sAGv0PkxNHVb1dSBfPP3D\_\_9918-Hc6rPmsjElcSywbMiH1m7nHm0UTKdp\_odjg\_XyfdrBGedM1fyf8HH170v1oeAAA" htmlZN="2" %}

## Sorting Parameters

All offset-paginated endpoints accept **`OrderByField`** + **`IsOrderByAsc`** for server-side sorting. Both are optional; omitting them returns results in the API's default order.

Sort Parameters (universal across all paginated endpoints)

| Parameter      | Type      | Description                                                                                                      | Required |
| -------------- | --------- | ---------------------------------------------------------------------------------------------------------------- | -------- |
| `OrderByField` | `string`  | Name of the field to sort by, such as `classificationCode` or `vatRate`. Must match a field on the returned DTO. | No       |
| `IsOrderByAsc` | `boolean` | `true` = ascending `(A→Z, 0→9)`. `false` = descending `(Z→A, 9→0)`.                                              | No       |

{% @code-walkthrough/html-renderer aspectRatio="2.5" htmlZ0="H4sIAAAAAAAAA60a227byPVdX3HC7IZSQlIkbflCiUod29mmSDZB7HRRBAEyJIfSrCkOMTOSrQQG8gEF-rIo2qe-9QP6UPShX5MfaD-hmOFFpCj5km2CSOSZM-d-m1FGD05eH5\_\_4c0pTMUsGXdG8gsSlE58DacahAni3NcSMpkKTS5jFI07oxkWCMIpYhwLX3t3\_tw80EpwimbY1xYEX2aUCQ1CmgqcCl-7JJGY-hFekBCb6sUAkhJBUGLyECXYdyxbkhFEJHh8RplAQYLhWMpAYhIiQWgK5xI46udInVFC0gtgOPG1jOGQpikOhQZThmNfmwqRca\_fj2kquDWhdJJglBFuhXSmlXtvR-2HnLtPYzQjydI\_efXkFU2pRwRKjMvJVPzGNnZse2gbu-pzYNtDRz4\_KjacLVPsKUSJsGfbw33bHh7Y9qOI8CxBS59fokzLdeBimWA-xVhZW72NO6CcYyknwOcOAIBpBhMPHsbqz7AA8TmLUYi3wV25YMduPCgXAsoizDx4GDnRIApKMApDnAoPHto2OtjfaYIllV20F8b7JVzgK4nsOM6BWwFnc4EjDx7uBfvugV1CQxrhQvKDGMVhCWcoInPugWNnVxJ2XeocIXbRVNkO7NCx2yo7O87ACTao7IRO7B60VHb3XLSzt0FlPEB2W-VBsB\_GaE1lfIBRbN9VZTtyHGd\_u8odgMcGPPa8AMeUYfWIYoEZfIaAXpmcfCLpxINcBTOgV0OYITYhqQf2EDIURWrdHubEAhotC9sFKLyYMDpPIw8WiHWlLXu5JCFNKCuhUq8CLhPBzEPYA10GsW4ARyk3OWakiK0aT\_UuGEo5kWnq1XiCbbncyDmp50rf\_mP4-suXr798gZ8YyjLMytfH\_Q6AFeEZNS-LlW2aFK4uxC7962RXwGlColJfBW8gVS4ohKcLzOKEXnowJVGE0zuoVBBa16wUXVbLQu7KUnvZFThu7vMb1HGbogZUCDq7Ra2inngQJ7igjxIySU0i8Ix7IGMZs3zh5zkXJF6aRXX2gGcoxGaAxSUuVZ-gzIODUlRJVDnDA\_n5662jKnhhHhVtnHzCHjg7FUcJvMSy7HkgC-e9taxrsMa4TDP4DJUN9K9\_\_qc-rAtzmF0NmymSl4TesEaPz4OWLlXmnLwC2S90A2Y0pcrKw5bKTqlyg5UqKL2m7LWk3hJSG-yjXBcRhsPcUyFN5rO6k\_dqNWiVk6rTNjNSdWQVBfC5ShjzygM0F7SwCadMmHnrzgVVvV5WOvv7RlCHNElQxrESSD1tKj13MqBb83GNv5iu22rQsJWsd6aKHg8SHIuNOefelHNbHLbFvQkWAjNTKqGksewBnhWE5oxLShklq\_idc4mNExwKD1Ka4nbW5dllOQOer11OicCKA5ZbylRtW8abSv-p8G91gA3oFgoFWWBTAtc3NXNCbSNpJAc2KhnkTcqUBvZgV2bUeoa12LXK5m6zbN6jKt7Y4NYdtLWeFfZtCcq8BHFhhlOSRErsddmU19YGmnUSuSfy3bWGwCYB6rqDgVH-s2y3V6OVD4T3oGUb8m9JpZHr796-BDoX2Vw0E37OErOAb2vAxYTzKxrwXmn-prvtRiP4xop6SVlkBgyjCw\_Ul4mSpMhGkmJzWvQXxyrG4hlJK6C7e2tg3NDoGgY-W6YCXTWMW3fiNDGnlLcyq6goLZ9PEzNDsrqV6A939nedgbMR8wIvYXPKbgqnaWIuULJlw2Z8jrMW\_kryRujfWdEKe03P8DByosNNiE01H-4fhnYcb0K8m3oV-q3a1TrmFM8wCDqZtBqnXDGLlXYmrWr7t-SPW2XKxi5y74lpSz66\_4983NowGxm2nlDrp4ctrfF63dRVk6tTbPl8cwcc9Yvz96hfXHvI4Wvc6YwisihvRupHFE0e1VuLcq9aARjxDKWNRTU3arfddcht2yiUw6c2Pk5IeAGSHWYcBC3j8LXU\_NnyOcFJBI\_QLBvCC14Aj3jYIB\_MhaAVg7olNaBpKDn4Wg5Qsd7tFaqVopGo2PYipKk2\_u\_f\_vjXOoMW2ksU4EQbn8hkm9GoqWw\_F0eZtR-RxUb7SqeU9q2trSbVlYS5jQuEVdusECRK7mqAFYTVXxXKyhSSyLNlV5c9UO9pJU6unuqM2vj49clp03HNCUlT6JwU6IUFRn0xvQPjCPOQkUxGi-RfMK5BtfHJ6dnx2xdvzl-8\_vFOYjQ2308ahieFIHUz5FBt\_Pb0h7vKUG65H\_sFEm-RqBxRsC-g2vj3R-fw9uj8bs6odm0RYdSvB4ZcbQTOSKiTmiJGmVCJ\_ExF6qgv8jpS7ZRrZcQXUd6M5dUQplUU37FE0iqzIn8ovjqj3IPjTkhTLkBxOEECgQ\_vO3kzi7AH-p5r71p7jm5AzekeaD\_RGU51DoJRWWe5ATSGS0oTzYDcNR7op-90AwozeeA6lg3Xxhr1HWvXbVF\_1aYdymk5rVN\_d1anbls2rFN3HNty7XXq-u8w43jJV1R1o3JaRf2HZw3Z7bbsjrTMzlbLBCSQHPgyFVMsSKgZcB\_L7NqudXjYkv31XF73cZpgDmweBJithK-oHzWoOxtlP7B22nYvZU\_l4BYxzLnWpn58VKc-2GB313asw3bM5F5FKWXooka4Sf12y-zYruW2qOvPcKTm9dSAjMnZJtpgmc0x82HY6SRYgMyavAn6kM6TxFCQIx6CD4LN8bDTieepuiKBoqLEEr2n5jUSQ7dGwfehWFwReVA8ykkEJ1xeLdV5KvzhOlM1szCcRph1e8POdU2IEqr4v897jNGo-EZZcY2q9n2wYspOUTjthuCPi1EzouF8hlNhTbA4TbB8fLZ8EXV1MTX1J2HPUoXmJeHCypt7V68d93UDQqVxpU154biNLCc5WTlLHee3a-CvE4GnuUWlMZ7Ku7d\_6KDu4P6t9-SDria63lCO2Hkdk9hYmvK9ZVlVUfugTsDdLjIg6K10lg57sJIYGBZzlpaXvTlBtAAf0PsK64MB" htmlZ1="gQQFNdCwoiaWGaax2uT7oKfzmUzQivJKF7QAUxLy5IcJaJHTaOGdCUbSSRctelZC5S9gx3SWIYa7xUKw6ElLrN42o6FFr1ez1Xav1BuR3rNImmL22\_NXL8EvTGvNUNZl0oYfO2vDz0hE4-8-M0tG4fWoL6L1hVpcblzPI3XjUhG7lqDPyRWOuk7v-vsVXtlpP\_asnylJu7pej4k5S04T8G\_W-h1LdGWifE-AOAbw4WNjEChOptr465e\_l01\_bV2eRbVxH2Wkv3D7zWGdb9nDcaaNn25ZvMBLbfwGTfCPKphuIuFvWVygRBs7N-1UM\_9tApyRT\_jb2dvF6kdpZOWSVnSpbFIOfZo74Enb\_ncTt36g-WaRv\_tcCXX9q4y34Sz1rdIc8bCUJc8\_T1mq2RUapy9V6\_KgVr\_cQz0TyociHVbxT7g6b\_n5Rceq9ssfQhBJeVeXlx95xqyhlO1BIRjwICd1E6a6JtINqGHe0I6Kk6Pe7B3glzI\_BV2eKfNO8Zcv\_\_nXn\_TbKapDpr7ejlYUq9OnIvtS3WupV2X5VXMe9cu5etTPZ\_hRX\_2\_if8BY0GwZUchAAA" htmlZN="2" %}

## PagedResult Response Schema

Every offset-paginated endpoint wraps results in a **`PagedResult`** envelope. The envelope provides all metadata needed to drive a paginator UI.

<details>

<summary>Customs.Catalog.Domain.Pkg.Models.Paging.PagedResult</summary>

| Property       | Type    | Description                                                                                   |
| -------------- | ------- | --------------------------------------------------------------------------------------------- |
| currentPage    | integer | The current page number (1-indexed). Use to highlight the active page in a paginator control. |
| pageCount      | integer | Total number of pages available = ceil(rowCount / pageSize). Use to render the page range.    |
| pageSize       | integer | How many records were requested per page. Reflects the PageSize parameter sent.               |
| rowCount       | integer | Total number of records matching the query filters. Use for "Showing X–Y of Z" labels.        |
| firstRowOnPage | integer | 1-based index of the first record on this page. Pre-computed for display labels.              |
| lastRowOnPage  | integer | 1-based index of the last record on this page. Pre-computed for display labels.               |
| results        | array   | The data payload for the current page. Length ≤ pageSize.                                     |

</details>

### Sample PagedResult Response

{% code overflow="wrap" expandable="true" %}

```json
{
  "currentPage": 2,
  "pageCount":   10,
  "pageSize":    25,
  "rowCount":    247,
  "firstRowOnPage": 26,
  "lastRowOnPage":  50,
  "results": [
    { "classificationCode": "6204.61", "description": "Women's trousers of wool" },
    // … 24 more items
  ]
}
```

{% endcode %}

## URL Builder

{% @code-walkthrough/html-renderer aspectRatio="2.5" htmlZ0="H4sIAAAAAAAAA61a6Y4bNxL-r6dg2oZbsnW0NJ7DrcPrObI7gK-dI0B2YMRUd7XETItsNNmaUSYD-P8G2B8JFhtggbzFYp\_HL7B5hAWPvqSWZiaJf3i6yWKR9VXVVyRbgy8O3x2cff3-CE3FLBzVBvIPCjGdDC2gFvJCzPnQCslkKizZDdgf1QYzEBh5UxxzEEPr\_OzL1p6VNlM8g6E1J3AVsVhYyGNUABVD64r4Yjr0YU48aKmXJiKUCILDFvdwCMNu25FqBBEhjA4SLtiMowMscMgm6NX7Y\_T500\_o\_OQ12k9I6EM86GjR2iAk9BLFEA6tKAaPUQqesNA0hmBoTYWIuNvpBIwK3p4wNgkBR4S3PTaz0rF3i3Y8znsvAzwj4WJ4-ObZG0aZSwQOm1eTqfiT09xynL7TfK7-33acflc-PzEDThcUXCUoBXYcp7\_rOP09x3niEx6FeDHkVziytA1cLELgUwCFuXob1RDqPEWff\_r0-adP6BDHl0hMYQao7kOAk1A00r6nnRpS3mz7UuqmhhBCrdZ44qJHztjxuk7fNPEkDrAHLnrU3epud8dL7T3Z4XWD3l7aMWaxD7GLHvV2enhrJ23GngdUSPUObGOn3Cy1bI93vQCn7QKupTDsAQ4y4VkiwHfRo53xbm8va\_WYD2blfrfb3U3bY-yThLuo60TXsu22VoLntQxXg88KKiqWy7AE6t8qLJXtPbN2gBVY\_K6\_7Y8rYMF7u1ursDzHO16wuwRLt9vd6-3eF5ZgL8CBtxGWp0301HXHELAY1CMOBMToBo3ZdYuT7widuEib0Bqz6z6a4XhCqIucPoqw76t-p6-VjZm\_MNiNsXc5iVlCfRfNcVyXWDb0SjwWsjhtlXaZdplWLZ0QLrJlSthNxDHlLQ4xMUBnc0orUPe5NgUhEWPKiSCMuoW5kdPu8aaeUT1XhMMhzBi6inEUQVyKh7YPM9ZKe9aZZfxubEid3Y2uEWch8VPjVXtJKPOHltBvRoLNIQ5CduWiKfF9oPew0SitMFXbIbnZGJFhuFeCcK1tvfK6x0wINrvDRsNbLgpCMPpxSCa0RQTMuItklEOsO75NuCDBomVqgYt4hD1ojUFcQWq7VKN84Spf6caJfN25VwjcBY8qFQYfFYicfAcu6m6l2lXjFUh6cJFk6AebqVa7l-ZeaeI0A9ENykCwP\_\_zP3a\_uJgX0XW\_nD2aLRr9oiWFJMyTpVfydMWqFb4-icHTAHosTGa0auGl5DlgVMQs5OXE8dLWm6WpVtyodStGKgOnBIH6fQ1VQEIBMUcP06hR8UQctmQwRGtGr5qtlWxpHWUVIR5DWAyUjLEO3yBZ9e0mmjHKVAz3V-Kpm\_qg5EdF5CZzQhAC4pYcr3zXdp7DLI\_WwlIIjRLRXGrkEIIn1tKVKQ6\_g652Ki1Yy-P3QqWX6swidlsGrJM2q\_2glMtaWCJCQsFFlFGoyP9iumsCuKsoVGDrBsxLeDXCuk8Vynyqirys9k5qUc9RgeolMZejI0YUXxhfj-VGtjUWdK0704mKDint41IP5zCtc-e9ym-l00rUuJtS42ocb-k4Lnh5V3p5LwuoJRQKdMohDAqksOJvJmcRC-1q1RGweIba3dy5GZjuVJZXSSVmlIvae3v9fJirH0Ms4Ot6qxtdp47MVWBPkLmk6-pBTkrJOVO-S0SUCBSzqzJXMtXeku0r7JQRb5kbuYhBeNM0SpI4bGktKS2FcO2i7rqq\_gdSQHkn0asOpYcxwBWL\_dY4BnzpIvWnhcPQRBSh0JqaOOu2zQljRmjWuHW\_\_eCGzYDHosXGjKvcE\_0RDFqsAZWZ8HuqTeaonZKjrqZEgEpRxaT5zkplGp\_GhF7K7f0d\_NrdzvhUPxcQlw05wRp08xTcyJ4VFaasRz4Q8B-kp7xnyhP0TB0IBZtMwvK5sK2Oii3TsxoXy9T6sGDIS1ql0x-8wVzNzee\_Nzfv3rOsDY2qUotQwmVh0GU0g-92GWoTJL8pRkqePV1Qga\_RlEym-nhffRfSnoatKeNCbcJXDVULLEtHWExz6UfeC7\_rv6gSvIQFKgjuvvCcIKgSnOMQLc9f2kyUxTlEK-L5cst3Gvc2Lxdfsm9r93l3u1spWTawuO5eter7GZrLb7Z00DHXYIOOuYOUp6BRrTbwyTy9piwe5y15Y7bSKceqHoQGPMK01KkOatborwnEi\_yKUYqZEeNECJaNKUayhRj1QuJdDi3doLim3jBzpbMR3ww79hi1Rr\_-8sPPxQlWxF7Lo4g1Utd9M-ZDaTUdvRxlZ8cn81GtymIJU2pxoS89wOULLHZm29msG6GBOheNjt4evn93\_PZs0NHveb\_Z-crFj4-or0hO4TLFdAJDK4l8LOBLfc4rQKMGs0hyC5rjMIGh1cER6cx7nQO5HhIQD8tebo2WGgYdPe7hqjoHUxwJiFdnqGx\_wETvY-YnnuDZwzmxRukz6qC8-bcoPaZzHBK\_pNG0obquL40H6P0KC2v0FRYPG7KPqX\_AaEAmanD-uqpm0NFhkYWZjtTNMWdC7f2rPx-ht-dv9o9O0mgbqFMbEosIhhZNZmOILR1x7\_EErHStXUtuGuXf0YMnPD3-29Hd052S7\_LpetvZfGiGr4dWz3EeNvO7k8OjE7T\_deXEsuaZad\_JOrm\_sFAUYg-mTFLU0AoIhP5bPIN7Trqc069OD1AHHR6dHmzO6lfcs5DiYfM1x-1uOdH1pky2RvKbjdwByI839wgzESdgjV5xD6jc3NxjRIBDDtboENaP2RyFZVrPjoAFTldt53EoWUsVBvkVqkjARQou427utbT\_0pfROun8rFhJzPlBUOszzyNdNoxndAUtFVC915Pd6osV8SSTEu8SWZk1FhIMTYBCjEVaZzbBlG7Q9ULk234JMtliEPv1lx\_\_jg5YtKhELHtIMRlwLyaRGNU8RrlAEJmSgYZqa26vYXTbRRf2CUwIowfMB\_tDc4PwGvpf1tG0Uzn9dqpvESv0V7C-" htmlZ1="UrYfK3JUow\_1l7rynHbTPgOKqdik1DD8isZTwLE3PVtEaqmMCkITpfWMXQJd0vYVFkqBYexj324u2coSKuLFMWerI3OOX9FhhhklJ0mozLjt12pBQhVeaKn0Kz9q7yrqkq7N3HzhMy-ZARXtCYijEOTj\_uLYr9vZrsJutFXuf0Dff48uPsizxdpBJuXsRptQCvFfzt68RkMzbXuGo3qAhjoeP97B0Y9vgrZg53JzeYA51Bu3d7F18M3jm-B2iax1U5lFtxWL6gT4WEOo0f6WEVq37Ua\_dlurhSBQiLmQySrO4xANkW0X8c35qQDtGHOQkulH7ZBNCBfE4y1Pf1r39Jf1Fo5Im2Pqj9l1G\_iURVcsDn35zdvuZ9rk7hwN1-O84pz-Jh9DVPCdNC\_CsZAiF8oVF7Ys529VwbWbGyaVYumEUqHd1aGbqpBl2m6iDfGhKnlJRW9b61BrM4ESsPgIe1MVLPoTjoQk3AhI8I39LGj0EQlQHcKXeoaGtrQdJXxavwiaCEITy\_KY08hBY-PNcJudwCramHsbB77iXnGQXB0bN1BpXbbR\_qW03m4iNv7QSIUx98pG2MfciEvVTbkAKS0dW47Zj49vZEzePr6B6PblR\_TMqJFZWL-4bM4\_NCS8Hx\_fXN4OH98AlfeY5yfHB2wWMQpU1OeN249pbjyx9SRrLdXFsZz4JtGLRz9zaLZGZnVp7VuSkSdlKQPROgkOkTV6aTo\_omfmjqZsYZM0Ur5B0lIycl7alZqe4FnUN9ps17Zvl6UuYSEXdLlxPcM1nXMcKmsqMU5tUMssclHOLYJuCjKzHbBV0KhbPDnxa8JFO4YZm4MSIeDnEpI2D\_SXUUla2Z7BVgyYMV22rVBEJ-Pxi2KUNVAMIonVZ02K52SCBYvbXkiiMcOx376KiYAzuBb10ih5K0XrdRV-N-Yi7KFmVprx-d8\_SiukpblMDgb2\_TISCHEQZ2QGLBHpeu6A5y580W1TfoRylP7bRhnO0mVFoXaon4GVCCh9MADkfEO4up4Y6vucfCHyhgETyuu2vMzS5i2J6NmNQBN9oVVtklTXRXYTFSTXeie7aLEbJfjQMF3zSwnkDz\_bSH6L\_9en\_\_33H\_bdGtWdzJLKosbsskap1T9FUq8K-aWdUF9ebJnt7qCjr7QGHfVbvP8D6GJo55snAAA" htmlZN="2" %}

## Code Samples

Ready-to-use implementations for offset pagination, cursor iteration, and sort-aware fetching.

{% tabs %}
{% tab title="Offset Pagination Helper" %}
{% code overflow="wrap" expandable="true" %}

```typescript
interface PagedResult<T> {
  currentPage:    number;
  pageCount:      number;
  pageSize:       number;
  rowCount:       number;
  firstRowOnPage: number;
  lastRowOnPage:  number;
  results:        T[];
}

async function fetchClassifications(
  page: number,
  pageSize: number,
  regionCode?: string,
  orderBy?: string,
  asc?: boolean
): Promise<PagedResult<ClassificationDto>> {
  const params = new URLSearchParams({
    PageNumber: String(page),
    PageSize:   String(pageSize),
  });
  if (regionCode) params.set('RegionCode',    regionCode);
  if (orderBy)    params.set('OrderByField',  orderBy);
  if (asc !== undefined) params.set('IsOrderByAsc', String(asc));

  const res = await fetch(
    `https://logistics-customscatalog-api.sandbox.eshopworld.com/api/v2/Classifications?${params}`
  );
  return res.json();
}
```

{% endcode %}
{% endtab %}

{% tab title="Cursor Iterator (Products/Invalid)" %}
{% code overflow="wrap" expandable="true" %}

```typescript
async function* iterateInvalidProducts(
  brandCode: string,
  pageSize = 25
) {
  let page  = 1;
  let token: string | undefined;

  do {
    const params = new URLSearchParams({
      BrandCode:  brandCode,
      PageNumber: String(page),
      PageSize:   String(pageSize),
    });
    if (token) params.set('ContinuationToken', token);

    const res  = await fetch(`…/api/v2/Products/Invalid?${params}`);
    const data = await res.json();

    yield data.results;         // yield this page's records
    token = data.continuationToken;
    page++;
  } while (page <= data.pageCount);
}

// Usage
for await (const page of iterateInvalidProducts('BRAND_A')) {
  console.log('batch:', page);
}
```

{% endcode %}
{% endtab %}

{% tab title="Generic Pagination Hook (React)" %}
{% code overflow="wrap" expandable="true" %}

```tsp
function usePagination(defaultPageSize = 25) {
  const [page, setPage]     = useState(1);
  const [size, setSize]     = useState(defaultPageSize);
  const [orderBy, setOrder] = useState<string>();
  const [asc, setAsc]       = useState(true);

  const toggleSort = (field: string) => {
    if (orderBy === field) setAsc(v => !v);
    else { setOrder(field); setAsc(true); }
    setPage(1);  // reset to page 1 on sort change
  };

  const queryParams = () => ({
    PageNumber:    page,
    PageSize:      size,
    OrderByField:  orderBy,
    IsOrderByAsc:  orderBy ? asc : undefined,
  });

  return { page, setPage, size, setSize, orderBy, asc, toggleSort, queryParams };
}
```

{% endcode %}
{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developer.esw.com/customs-catalog-api/pagination-patterns.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
