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 types — status, 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.