Skip to main content

Submit a Product

This guide shows you how to submit a product to noon using UpsertProduct. By the end, you'll have a product stored in noon's catalog with a sku_parent you can use to track its status.

Prerequisites

Step 1 — Submit the product

UpsertProduct (POST /content/v1/product/upsert) creates a product if the partner_sku values are new, or updates the existing product they belong to. Pass X-Project as a required header to scope the call to your project.

The example below submits a two-size shoe product with localizable title and brand attributes.

# 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/product/upsert",
json={
"skus": [
{"partner_sku": "MY-SNK-42", "size": "42"},
{"partner_sku": "MY-SNK-43", "size": "43"},
],
"brand": "Acme",
"category": "apparel-shoes-sneakers",
"images": [
{"url": "https://cdn.example.com/acme-runner-main.jpg", "sort": 1},
{"url": "https://cdn.example.com/acme-runner-side.jpg", "sort": 2},
],
"attributes": {
"product_title": {
"values": [
{"value": "Acme Runner Sneaker", "language": "LANGUAGE_EN"},
{"value": "حذاء اكمي رانر", "language": "LANGUAGE_AR"},
]
},
"long_description": {
"values": [
{"value": "Lightweight everyday runner with cushioned sole.", "language": "LANGUAGE_EN"},
{"value": "حذاء خفيف الوزن للاستخدام اليومي مع نعل مبطن.", "language": "LANGUAGE_AR"},
]
},
},
},
headers={
"User-Agent": "MyCatalogApp/1.0.0",
"X-Project": "<project_code>",
},
timeout=30,
)
response.raise_for_status()
result = response.json()
sku_parent = result["sku_parent"]

Response:

{
"sku_parent": "Z1ABC234",
"variants": [
{"sku": "N12345678", "partner_sku": "MY-SNK-42", "psku_code": "P0012345", "size": "42"},
{"sku": "N12345679", "partner_sku": "MY-SNK-43", "psku_code": "P0012346", "size": "43"}
],
"status": {"status_id": 0, "status_code": "OK", "message": ""}
}

You'll know this worked when status.status_id is 0 and sku_parent is present in the response.

Store sku_parent immediately. It is the only handle accepted by GetContent and the stable identity for all future updates to this product.

ConditionWhat it meansWhat to do
Non-2xx responseRequest failed entirely — auth error, missing X-Project, quota exceeded, or invalid categoryCheck rpcStatus.message. For quota errors, the message states your limit and period.
status.status_id: 3 (INVALID_ARGUMENT) within a 200Product was stored but has content problemsNote the sku_parent, then call GetContent for the detailed breakdown — see Track and Fix.
warning

A 200 response does not mean your content is valid. Always check status.status_id0 means accepted cleanly; 3 means stored with problems.

SKU and size rules

  • Single-SKU product: size is optional. Omit it if your product has no size dimension.
  • Multi-SKU product: every SKU in the request must have size set.
  • Updates: all partner_sku values in a single request must belong to the same parent product. noon uses partner_sku as the identity key — sending an existing partner_sku triggers an update, not a create.
  • partner_sku must be globally unique across all your products, not just within a single product.

Structuring attribute values

Attributes are sent as a map of attribute_code{ "values": [...] }. How you populate values depends on the attribute's flags from ListCategoryAttributes:

Non-localizable, single value:

"colour": { "values": [{"value": "Red"}] }

Localizable (is_localizable: true) — one value per language:

"product_title": {
"values": [
{"value": "Acme Runner Sneaker", "language": "LANGUAGE_EN"},
{"value": "حذاء اكمي رانر", "language": "LANGUAGE_AR"}
]
}

Multivalued (is_multivalued: true) — use sort to order values:

"feature_bullet": {
"values": [
{"value": "Cushioned sole", "language": "LANGUAGE_EN", "sort": 1},
{"value": "Breathable mesh upper", "language": "LANGUAGE_EN", "sort": 2}
]
}

Metric (ATTRIBUTE_TYPE_METRIC) — numeric value and unit as separate attributes:

Metric attributes are always submitted as a pair: the numeric attribute and a corresponding _unit attribute. The unit value must exactly match one of the strings in attribute_metric_units from ListCategoryAttributes. Both must be present in the same UpsertProduct call — you cannot update one without the other.

"product_length": {
"values": [{"value": 10, "language": "LANGUAGE_EN"}]
},
"product_length_unit": {
"values": [{"value": "millimeter", "language": "LANGUAGE_EN"}]
}

Next steps