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
- Authentication set up — see Getting Credentials and Authenticating Your Requests
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.
- Python
- Node.js
# 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"]
// getAuthenticatedClient() is provided in docs/snippets/auth.mdx — see Authenticating Your Requests
const client = await getAuthenticatedClient();
const response = await client.post(
"https://noon-api-gateway.noon.partners/content/v1/categories/list",
{},
{
headers: {
"Content-Type": "application/json",
"User-Agent": "MyCatalogApp/1.0.0",
},
}
);
const categories = response.data.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.
- Python
- Node.js
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"]
const response = await client.post(
"https://noon-api-gateway.noon.partners/content/v1/categories/attributes/list",
{ category_code: "apparel-shoes-sneakers" },
{
headers: {
"Content-Type": "application/json",
"User-Agent": "MyCatalogApp/1.0.0",
},
}
);
const attributes = response.data.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.
| Condition | What it means | What to do |
|---|---|---|
| Non-2xx response | category_code was rejected or the request was malformed | Confirm the code is a value from Step 1 — codes not in that list are invalid. Check rpcStatus.message for details. |
Empty attributes array | The category exists but has no attributes exposed to the API | Contact 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_mandatory—truemeans 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_localizable—truemeans submit separate values per language:LANGUAGE_ENfor English andLANGUAGE_ARfor Arabic. Completeness is tracked independently per language.is_multivalued—truemeans the attribute accepts multiple values. Send them with asortfield starting at 1.max_valuescaps the count.is_facet—truemeans noon indexes this attribute for storefront search filters. Accurate values improve product discoverability.attribute_options— forATTRIBUTE_TYPE_SELECTattributes, the value you send must exactly match one of these strings (case-sensitive). A non-matching value fails validation.attribute_metric_units— forATTRIBUTE_TYPE_METRICattributes, 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_unitattribute must always be submitted together; you cannot update one without the other. See Submit a Product for the full example.
Attribute types:
attribute_type | What to send to UpsertProduct |
|---|---|
ATTRIBUTE_TYPE_TEXT | String 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_NUMERIC | Number within number_min / number_max; negative only if is_negative_allowed is true |
ATTRIBUTE_TYPE_SELECT | One of attribute_options exactly (case-sensitive) |
ATTRIBUTE_TYPE_METRIC | A 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_BOOL | true or false |
ATTRIBUTE_TYPE_DATETIME | ISO-8601 timestamp within date_min / date_max |
ATTRIBUTE_TYPE_FILE | Public URL; file within min_size_in_kilobytes / max_size_in_kilobytes and of a type in allowed_mimetypes |
ATTRIBUTE_TYPE_JSONDICT | A JSON object |
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
- UpsertProduct Reference — submit a product using the category and attribute mapping you've built
- GetContent Reference — check completeness, image review status, and QC state after submitting
- ListCategoryAttributes Reference — full request and response schema for this call