---
updatedAt: 2026-02-12T20:57:16.000Z
agentTools:
  projectIndex: https://apidocs.shipmonk.com/llms.txt
---

# Sync ShipMonk Inventory Levels

# Products for Inventory Sync - API Guide

## Overview

The Products for Inventory Sync API uses a **two-step cursor-based pattern**:

1. **Create a search** (`POST /v1/products/search`) - Submit your search criteria and receive a cursor token.
2. **Fetch results** (`GET /v1/products/search/paginate`) - Use the cursor to paginate through matching products and their inventory data.

This design lets you query large product catalogs efficiently without repeated filtering on every page request. Additionally, this lets you effectively sync a large catalog of products by only retrieving the subset of products where a change in inventory levels has occurred.

***

## Authentication

All requests require an API key passed via the `api_key_only` firewall. Accepted key types:

* Account Integrations API Key
* Public API Key
* Personal API Key

Include your API key in the request headers as required by your ShipMonk account configuration.

***

## Step 1: Create a Search

### `POST /v1/products/search`

Creates a search cursor that snapshots matching product IDs. The cursor is **valid for 24 hours**.

### Request Body (JSON)

All fields are **optional**. An empty body `{}` returns all products.

| Field                                | Type     | Description                                                                   |
| ------------------------------------ | -------- | ----------------------------------------------------------------------------- |
| `inventory_on_hand_updated_at_start` | datetime | Filter: inventory last updated after this time                                |
| `inventory_on_hand_updated_at_end`   | datetime | Filter: inventory last updated before this time                               |
| `date_updated_start`                 | datetime | Filter: product updated after this time                                       |
| `date_updated_end`                   | datetime | Filter: product updated before this time                                      |
| `date_created_start`                 | datetime | Filter: product created after this time                                       |
| `date_created_end`                   | datetime | Filter: product created before this time                                      |
| `search`                             | string   | Text search (1-255 characters) against product name/SKU                       |
| `status`                             | enum     | `active`, `disabled`, or `blacklisted`                                        |
| `sort_by`                            | enum     | `id` (default), `created_at`, `updated_at`, or `inventory_on_hand_updated_at` |
| `sort_order`                         | enum     | `ASC` (default) or `DESC`                                                     |

### Response

```json
{
  "cursor": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "total": 1250
}
```

| Field    | Type              | Description                            |
| -------- | ----------------- | -------------------------------------- |
| `cursor` | string (36 chars) | UUID cursor token - use this in Step 2 |
| `total`  | int               | Total number of matching products      |

### Example Request Body

```bash
{
    "status": "active",
    "inventory_on_hand_updated_at_start": "2025-01-01T00:00:00",
    "sort_by": "updated_at",
    "sort_order": "DESC"
}
```

***

## Step 2: Fetch Results

### `GET /v1/products/search/paginate`

Retrieves paginated product data using a cursor from Step 1.

### Query Parameters

| Parameter  | Type              | Required | Default | Description                       |
| ---------- | ----------------- | -------- | ------- | --------------------------------- |
| `cursor`   | string (36 chars) | Yes      | -       | Cursor from `/v1/products/search` |
| `page`     | int               | No       | `1`     | Page number (minimum: 1)          |
| `pageSize` | int               | No       | `50`    | Results per page (1-500)          |

### Response

```json
{
  "data": [ /* array of product objects */ ],
  "total": 1250,
  "pages": 25,
  "page": 1
}
```

### Product Object Fields

Each item in `data` contains:

| Field                          | Type              | Description                                                                       |
| ------------------------------ | ----------------- | --------------------------------------------------------------------------------- |
| `id`                           | int               | Product ID                                                                        |
| `sku`                          | string            | Product SKU                                                                       |
| `name`                         | string            | Product name                                                                      |
| `created_at`                   | datetime          | Creation timestamp                                                                |
| `updated_at`                   | datetime          | Last update timestamp                                                             |
| `inventory_on_hand_updated_at` | datetime or null  | Last inventory change timestamp                                                   |
| `is_active`                    | bool or null      | Whether the product is active                                                     |
| `replacement_cost`             | float or null     | Product replacement cost                                                          |
| `replacement_cost_currency`    | string or null    | Currency code                                                                     |
| `barcodes`                     | string\[]         | List of barcodes                                                                  |
| `country_code`                 | string or null    | Country of origin                                                                 |
| `stock_out_days`               | int               | Days out of stock                                                                 |
| `weight`                       | object or null    | `{ value, unit }` - units: `kg`, `g`, `lb`, `oz`                                  |
| `dimensions`                   | object or null    | `{ length, width, height, unit }` - units: `m`, `cm`, `mm`, `ft`, `in`            |
| `hs_codes`                     | object\[]         | `[{ hs_code, region }]` - regions: `us`, `uk`, `eu`                               |
| `custom_data`                  | object\[]         | `[{ name, value }]` - custom integration data                                     |
| `product_bundles`              | object\[]         | Bundle associations with SKUs and quantities                                      |
| `product_handling_units`       | object\[] or null | Handling units (`inner_carton`, `master_carton`, `pallet`) with weight/dimensions |

### Inventory Breakdown (`inventory`)

Each product includes an `inventory` object with totals and per-warehouse detail:

```json
"inventory": {
  "quantity_total_final": 500,
  "quantity_total_available": 480,
  "quantity_total_on_hand": 500,
  "quantity_total_quarantined": 10,
  "quantity_total_unavailable": 10,
  "locations": [
    {
      "quantity_final": 500,
      "quantity_available": 480,
      "quantity_on_hand": 500,
      "quarantined_quantity": 10,
      "quantity_unavailable": 10,
      "quantity_by_lot": [
        {
          "lot": { "lot_number": "LOT-001", "expiration_date": "2026-06-01" },
          "quantity_on_hand": 500
        }
      ],
      "warehouse": {
        "id": 1,
        "name": "Los Angeles",
        "identifier": "LAX"
      }
    }
  ]
}
```

<br />

***

## Typical Integration Flow

```
1. POST /v1/products/search        --> get cursor + total count
2. GET  /v1/products/search/paginate?cursor=...&page=1&pageSize=100
3. GET  /v1/products/search/paginate?cursor=...&page=2&pageSize=100
4. ...repeat until all pages fetched
```

### Key things to know

* **Cursor expires after 24 hours.** Create a new search if the cursor becomes stale.
* **The cursor is a snapshot.** Products added or updated after cursor creation won't appear in results. Create a fresh search to pick up changes.
* **Max page size is 500.** Default is 50.
* **Datetime fields default to America/New\_York timezone** for `inventory_on_hand_updated_at_start` and `inventory_on_hand_updated_at_end` if no timezone offset is specified.
* **An empty search body returns all products** in your account, sorted by `id` ascending.