Skip to main content

Discover Category Attributes

This guide walks you through the category discovery workflow — finding a valid category code and retrieving its full attribute contract. By the end, you'll know exactly which attributes noon requires for your product type and what validation rules each one must satisfy before you call UpsertProduct.

Prerequisites

Step 1 — Fetch valid category codes

ListCategories (POST /content/v1/categories/list) returns every valid category code in the catalog. The request body is empty.

# get_authenticated_session() is provided in docs/snippets/auth.mdx — see Authenticating Your Requests
session = get_authenticated_session()

response = session.post(
"https://noon-api-gateway.noon.partners/content/v1/categories/list",
json={},
headers={"User-Agent": "MyCatalogApp/1.0.0"},
timeout=30,
)
response.raise_for_status()
categories = response.json()["categories"]

Response:

{
"categories": [
"apparel-shoes-sneakers",
"electronics-mobiles-smartphones",
"home-furniture-sofas"
]
}

Each string is a complete family-product_type-product_subtype path. Find the code that matches your product type and carry it into Step 2.

You'll know this worked when categories is a non-empty array of strings.

Step 2 — Fetch attributes for your category

ListCategoryAttributes (POST /content/v1/categories/attributes/list) returns every attribute noon accepts for the given category — mandatory and optional — with the validation rules each value must satisfy.

response = session.post(
"https://noon-api-gateway.noon.partners/content/v1/categories/attributes/list",
json={"category_code": "apparel-shoes-sneakers"},
headers={"User-Agent": "MyCatalogApp/1.0.0"},
timeout=30,
)
response.raise_for_status()
attributes = response.json()["attributes"]

Response:

{
"attributes": [
{
"attribute_code": "product_title",
"attribute_type": "ATTRIBUTE_TYPE_TEXT",
"is_mandatory": true,
"is_localizable": true,
"is_multivalued": false,
"is_facet": false,
"min_characters": 10,
"max_characters": 200,
"is_html_allowed": false,
"allowed_html_tags": [],
"attribute_options": [],
"attribute_metric_units": [],
"allowed_mimetypes": []
},
{
"attribute_code": "shoe_size",
"attribute_type": "ATTRIBUTE_TYPE_SELECT",
"is_mandatory": true,
"is_localizable": false,
"is_multivalued": false,
"is_facet": true,
"attribute_options": ["36", "37", "38", "39", "40", "41", "42", "43", "44", "45"],
"allowed_html_tags": [],
"attribute_metric_units": [],
"allowed_mimetypes": []
}
]
}

You'll know this worked when attributes is a non-empty array and each object contains attribute_code, is_mandatory, and attribute_type.

ConditionWhat it meansWhat to do
Non-2xx responsecategory_code was rejected or the request was malformedConfirm the code is a value from Step 1 — codes not in that list are invalid. Check rpcStatus.message for details.
Empty attributes arrayThe category exists but has no attributes exposed to the APIContact noon — this is unexpected for any active category.

Reading the response

Build your attribute mapping from this output. The fields that determine what you must send to UpsertProduct:

  • is_mandatorytrue means this attribute is required. A product missing any mandatory attribute cannot reach 100% completeness and won't enter QC. Map all mandatory attributes before submitting.
  • attribute_type — the format noon expects for the value. See the type table below.
  • is_localizabletrue means submit separate values per language: LANGUAGE_EN for English and LANGUAGE_AR for Arabic. Completeness is tracked independently per language.
  • is_multivaluedtrue means the attribute accepts multiple values. Send them with a sort field starting at 1. max_values caps the count.
  • is_facettrue means noon indexes this attribute for storefront search filters. Accurate values improve product discoverability.
  • attribute_options — for ATTRIBUTE_TYPE_SELECT attributes, the value you send must exactly match one of these strings (case-sensitive). A non-matching value fails validation.
  • attribute_metric_units — for ATTRIBUTE_TYPE_METRIC attributes, the unit is submitted as a separate attribute named <attribute_code>_unit. The value of that unit attribute must exactly match one of these strings (case-sensitive). Both the numeric attribute and the _unit attribute must always be submitted together; you cannot update one without the other. See Submit a Product for the full example.

Attribute types:

attribute_typeWhat to send to UpsertProduct
ATTRIBUTE_TYPE_TEXTString within min_characters / max_characters; match additional_validation_regex if present; HTML only if is_html_allowed is true, restricted to allowed_html_tags
ATTRIBUTE_TYPE_NUMERICNumber within number_min / number_max; negative only if is_negative_allowed is true
ATTRIBUTE_TYPE_SELECTOne of attribute_options exactly (case-sensitive)
ATTRIBUTE_TYPE_METRICA number; the unit is submitted as a separate attribute named <attribute_code>_unit whose value must exactly match one of attribute_metric_units — both must always be submitted together
ATTRIBUTE_TYPE_BOOLtrue or false
ATTRIBUTE_TYPE_DATETIMEISO-8601 timestamp within date_min / date_max
ATTRIBUTE_TYPE_FILEPublic URL; file within min_size_in_kilobytes / max_size_in_kilobytes and of a type in allowed_mimetypes
ATTRIBUTE_TYPE_JSONDICTA JSON object
info

The list is pre-filtered to attributes you can submit. If an attribute code doesn't appear here, don't send it in UpsertProduct.

Next steps