Skip to content

List Products

The canonical admin product listing — a paginated, filterable, and sortable product list that mirrors the Bagisto admin Catalog → Products datagrid 1:1. Same columns, same filters, and the same sort options used by the admin screen. This is the listing you want for product-management screens.

For the product types, the two-step create flow, status versus visibleIndividually, and the per-product sub-resources, see the Products overview.

A separate slim search, GET /api/admin/products, powers the Create-Order "Add Product" modal only. It is not the listing.

Endpoint

EndpointMethodAuthentication
/api/admin/catalog/productsGETAdmin Bearer token

Query Parameters

ParameterTypeDescriptionExample
pageintegerPage number (1-based, default 1)1
per_pageintegerItems per page (default 10, max 50)10
product_idstringFilter by product ID — single integer or comma-separated list142 or 1,2,3
skustringPartial SKU match (SQL LIKE %value%)SP-001
namestringPartial product name match (SQL LIKE %value%)Classic Watch
typestringFilter by product typesimple
statusintegerFilter by status: 0 = disabled, 1 = enabled1
attribute_familyintegerFilter by attribute family ID1
channelstringChannel code for locale/price resolution (default: current channel)default
localestringLocale code for name/category resolution (default: app locale)en
price_fromnumberMinimum price filter (inclusive)10.00
price_tonumberMaximum price filter (inclusive)500.00
pricestringPrice range shorthand — "min,max". Overridden by price_from/price_to when both are present10,500
sortstringColumn to sort by (see Sorting section below)product_id
orderstringSort direction: asc or desc (default desc)desc

Valid type values

simple, configurable, bundle, grouped, downloadable, virtual, booking

Response Shape

Responses use the standard admin { data, meta } envelope.

meta object

FieldTypeDescription
currentPageintegerCurrent page number (1-based)
perPageintegerNumber of items on this page
lastPageintegerTotal number of pages
totalintegerTotal matching products
frominteger1-based index of the first item on this page
tointeger1-based index of the last item on this page

Row fields (data[])

FieldTypeDescription
idintegerProduct ID
skustring|nullProduct SKU
namestring|nullProduct name (resolved via locale and channel)
typestring|nullProduct type (e.g. simple, configurable)
statusinteger|null1 = enabled, 0 = disabled
pricestring|nullRaw price value (decimal string, e.g. "3000.0000")
formattedPricestring|nullLocale-formatted price (e.g. "$3,000.00")
specialPricestring|nullRaw special (sale) price as a decimal string; null if none
formattedSpecialPricestring|nullLocale-formatted special price; null if none
specialPriceFromstring|nullStart of the special-price window; null unless a dated window is set
specialPriceTostring|nullEnd of the special-price window; null unless a dated window is set
quantityintegerSum of inventory qty across all inventory sources
baseImageUrlstring|nullStorage URL of the product's first image; null if no images
imagesCountintegerTotal number of images attached to the product
categoryIdinteger|nullID of the first category this product belongs to; null if uncategorized
categoryNamestring|nullTranslated name of that category (resolved via locale); null if uncategorized
channelstring|nullChannel code used for resolution
localestring|nullLocale code used for resolution
attributeFamilyIdinteger|nullAttribute family ID
attributeFamilyNamestring|nullAttribute family name
urlKeystring|nullURL slug (e.g. acme-drawstring-bag)
visibleIndividuallyboolean|nullWhether the product appears in category/search listings
shortDescriptionstring|nullShort description, HTML
descriptionstring|nullFull description, HTML
metaTitlestring|nullSEO meta title (empty string when unset)
metaDescriptionstring|nullSEO meta description (empty string when unset)
metaKeywordsstring|nullSEO meta keywords (empty string when unset)
weightnumber|nullProduct weight
featuredbooleanWhether the product is flagged as featured
newbooleanWhether the product is flagged as "new"
createdAtstringCreation timestamp
updatedAtstringLast-update timestamp

Four traps in these values:

  • price and specialPrice are decimal strings, not numbers — "3000.0000". Everything else numeric on the row (quantity, imagesCount, weight, attributeFamilyId) is a real number, so cast the two price fields before arithmetic or comparison.
  • specialPriceFrom and specialPriceTo stay null unless a dated window is set. A product can carry a specialPrice with both dates null — that means the sale price applies indefinitely, not that there is no sale.
  • categoryId and categoryName describe the first category only. A product in three categories reports one, matching what the datagrid shows. Read the full set from the categories block on Product Detail.
  • quantity is a sum across inventory sources, not a per-source figure. For the breakdown use List Inventories.

The same product over GraphQL comes back with different JSON typesstatus, quantity, weight, featured, and new are all strings there, and timestamps are ISO 8601 rather than the 2026-04-19 11:56:43 form used here. A parsing helper written against this endpoint will not survive being pointed at adminCatalogProducts.

Fields That Stay Null on the Listing

Every row carries all 55 keys, but the following resolve only on the single-product endpoint GET /api/admin/catalog/products/{id}:

  • taxCategoryId, manageStock, inStock, attributes, bookingProduct, warnings
  • the relation blocks: translations, images, videos, categories, inventories, customerGroupPrices, superAttributes, variants, bundleOptions, linkedProducts, downloadableLinks, downloadableSamples, customizableOptions, channels, relatedProducts, upSells, crossSells

They are present as null rather than omitted, so a key-existence check is not a substitute for a null check.

Sorting

Two forms are accepted — choose whichever suits your client:

FormExample
Compound sort param?sort=name-asc
Separate sort + order params?sort=name&order=asc

When both order and a compound sort value are present, the explicit order param takes precedence.

Sortable columns:

sort valueSorts by
product_idProduct ID (default)
nameProduct name
skuSKU
pricePrice
quantityInventory quantity (SUM across sources)
statusEnabled/disabled status
typeProduct type
attribute_familyAttribute family ID
channelChannel code

Pagination

  • Default page size: 10 items
  • Maximum page size: 50 items
  • Use ?page=N for page navigation and ?per_page=N to control page size

Errors

HTTP StatusCause
401Missing, malformed, expired, or revoked admin Bearer token

The listing never rejects a bad filter, but the three ways it can go wrong are not the same:

  • An unknown parameter is ignored. A misspelled ?tpye=simple returns 200 and the unfiltered list — no error, no hint.
  • An out-of-range status is dropped. ?status=7 returns the full unfiltered count, not zero.
  • An unrecognised type is applied and matches nothing. ?type=notatype returns total: 0. It does not behave like status, so an empty result may mean a typo rather than an empty catalog.

?per_page=999 is clamped to 50 rather than rejected.

Search Engine

The listing always queries the database. Configuring the admin panel to use Elasticsearch for catalog search does not change this endpoint's behaviour.

How It Differs From the Storefront Listing

  • No implicit status = 1 filter. GET /api/shop/products hides disabled products; this endpoint returns every status so an admin can find a draft or disabled product. Pass ?status=1 for enabled only.
  • Products with no inventory return quantity: 0, and products with no images return baseImageUrl: null with imagesCount: 0 — neither is excluded from the listing.

Released under the MIT License.