--- id: content-service-get-product-group title: "GetProductGroup" description: "Returns a product group: the attributes its products vary by, and the products themselves. Large groups come back a page at a time, so send the next_token from your previous response to read the rest." sidebar_label: "GetProductGroup" hide_title: true hide_table_of_contents: true sidebar_class_name: "post api-method" --- # GetProductGroup `POST https://noon-api-gateway.noon.partners/content/v1/product/group/get` ## Usage Plan | Type | Requests | Time limit (seconds) | | --- | ---: | ---: | | Rate | 1500 | 60 | | Burst | 1500 | 60 | Learn more about rate limits and usage plans in the [Rate Limiting](/docs/overview/rate-limiting) guide. Returns a product group: the attributes its products vary by, and the products themselves. Large groups come back a page at a time, so send the next_token from your previous response to read the rest. ## Request No parameters documented. ### application/json - `partner_group_ref` (string, required): Your reference for the group. Max 255 characters. - `sku_parent` (string, required): sku_parent of any product in the group. Max 64 characters. - `next_token` (string, required): The next_token from your previous response. Leave it empty to read the first page of products. Treat it as opaque: a token you build yourself is not rejected, it simply returns the wrong page. - `page_size` (integer (int32), required): Number of products per page. Default 50, maximum 200. ## Responses ### 200 A successful response. ### application/json - `partner_group_ref` (string, required): Your reference for the group. - `attribute_codes` (string[], required): Attributes the products in the group vary by, for example colour_name, model_name. Max 100. - `members` (object[], required): Products on this page, with their values for those attributes. At most page_size of them, never over 200. - `sku_parent` (string, required): sku_parent of the product, as returned by product/upsert. - `attributes` (object map, required): The product's value for each group attribute, keyed by attribute code. Single value for plain attributes. One value per language for localizable attributes. One value per entry, with its sort order, for multivalued attributes. Attributes the product has no value for are left out. - `next_token` (string): Send this back to read the next page of products. Empty when there are no more. Nullable. - `total_count` (integer (int32), required): How many products the group holds in total, across every page. ### default An unexpected error response. ### application/json The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors). - `status_id` (integer (int32)) - `status_code` (string) - `message` (string) - `details` (object[]) ## Code Examples ### python ```python # Authentiction code is added below, to know more about authentication # visit: https://noon-docs.noonpartners.dev/docs/authentication/authenticating-requests import json import time import uuid import jwt import requests BASE_URL = "https://noon-api-gateway.noon.partners" USER_AGENT = "NoonApiClient/1.0" with open("noon_credentials_sensitive.json", "r", encoding="utf-8") as file: credentials = json.load(file) # Create a signed RS256 JWT for the login request. def create_jwt(): return jwt.encode( { "sub": credentials["key_id"], # iat must be current Unix time in seconds; do not apply a timezone offset. "iat": int(time.time()), "jti": str(uuid.uuid4()), }, credentials["private_key"], algorithm="RS256", ) # Log in and return a requests session that keeps auth cookies. def get_authenticated_session(): session = requests.Session() session.headers.update({"User-Agent": USER_AGENT}) response = session.post( f"{BASE_URL}/identity/public/v1/api/login", json={ "token": create_jwt(), "default_project_code": credentials["project_code"], }, ) if response.status_code != 200: raise Exception(f"Login failed with HTTP {response.status_code}: {response.text}") return session session = get_authenticated_session() payload = '' headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' } res = session.post("https://noon-api-gateway.noon.partners/content/v1/product/group/get", data=json.dumps(payload), headers=headers) ``` ### go ```go // Authentiction code is added below, to know more about authentication // visit: https://noon-docs.noonpartners.dev/docs/authentication/authenticating-requests package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" "net/http/cookiejar" "os" "time" // go get github.com/google/uuid github.com/golang-jwt/jwt/v5 "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" ) const BASE_URL = "https://noon-api-gateway.noon.partners" const USER_AGENT = "NoonApiClient/1.0" type Credentials struct { PrivateKey string `json:"private_key"` KeyID string `json:"key_id"` ProjectCode string `json:"project_code"` } var credentials Credentials func check(err error) { if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func read_credentials() Credentials { file, err := os.ReadFile("noon_credentials_sensitive.json") check(err) var credentials Credentials check(json.Unmarshal(file, &credentials)) return credentials } func create_jwt() string { private_key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(credentials.PrivateKey)) check(err) token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{ "sub": credentials.KeyID, // iat must be current Unix time in seconds; do not apply a timezone offset. "iat": time.Now().Unix(), "jti": uuid.NewString(), }) signed_token, err := token.SignedString(private_key) check(err) return signed_token } func get_authenticated_session() *http.Client { jar, err := cookiejar.New(nil) check(err) client := &http.Client{Jar: jar} body, _ := json.Marshal(map[string]string{ "token": create_jwt(), "default_project_code": credentials.ProjectCode, }) req, err := http.NewRequest( "POST", BASE_URL+"/identity/public/v1/api/login", bytes.NewReader(body), ) check(err) req.Header.Set("User-Agent", USER_AGENT) req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) check(err) defer resp.Body.Close() response_body, _ := io.ReadAll(resp.Body) if resp.StatusCode != 200 { fmt.Fprintf(os.Stderr, "Login failed with HTTP %d: %s\n", resp.StatusCode, response_body) os.Exit(1) } return client } func main() { credentials = read_credentials() client := get_authenticated_session() url := "https://noon-api-gateway.noon.partners/content/v1/product/group/get" method := "POST" payload := strings.NewReader(``) req, err := http.NewRequest(method, url, payload) if err != nil { fmt.Println(err) return } req.Header.Add("Content-Type", "application/json") req.Header.Add("Accept", "application/json") res, err := client.Do(req) if err != nil { fmt.Println(err) return } defer res.Body.Close() body, err := io.ReadAll(res.Body) if err != nil { fmt.Println(err) return } fmt.Println(string(body)) } ``` ### java ```java // Authentiction code is added below, to know more about authentication // visit: https://noon-docs.noonpartners.dev/docs/authentication/authenticating-requests import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.fasterxml.jackson.databind.ObjectMapper; import okhttp3.*; import java.io.File; import java.security.KeyFactory; import java.security.interfaces.RSAPrivateKey; import java.security.spec.PKCS8EncodedKeySpec; import java.time.Instant; import java.util.*; public class Main { private static RSAPrivateKey readPrivateKey(String pem) throws Exception { String stripped = pem .replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s", ""); byte[] encoded = Base64.getDecoder().decode(stripped); return (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(encoded)); } static OkHttpClient getAuthenticatedClient() throws Exception { ObjectMapper mapper = new ObjectMapper(); Map credentials = mapper.readValue(new File("noon_credentials_sensitive.json"), Map.class); String privateKeyPem = (String) credentials.get("private_key"); String keyId = (String) credentials.get("key_id"); String projectCode = (String) credentials.get("project_code"); RSAPrivateKey privateKey = readPrivateKey(privateKeyPem); // iat must be current Unix time in seconds; do not apply a timezone offset. long now = Instant.now().getEpochSecond(); String jti = UUID.randomUUID().toString(); String token = JWT.create() .withSubject(keyId) .withIssuedAt(java.util.Date.from(Instant.ofEpochSecond(now))) .withJWTId(jti) .sign(Algorithm.RSA256(null, privateKey)); OkHttpClient client = new OkHttpClient.Builder() .cookieJar(new CookieJar() { private List cookies = new ArrayList<>(); @Override public void saveFromResponse(HttpUrl url, List cookies) { this.cookies = cookies; } @Override public List loadForRequest(HttpUrl url) { return cookies != null ? cookies : new ArrayList<>(); } }) .build(); Map loginPayload = new HashMap<>(); loginPayload.put("token", token); loginPayload.put("default_project_code", projectCode); RequestBody loginBody = RequestBody.create( mapper.writeValueAsString(loginPayload), MediaType.parse("application/json") ); Request loginRequest = new Request.Builder() .url("https://noon-api-gateway.noon.partners/identity/public/v1/api/login") .post(loginBody) .header("User-Agent", "REPLACE_WITH_YOUR_USER_AGENT") .build(); try (Response loginResponse = client.newCall(loginRequest).execute()) { if (!loginResponse.isSuccessful()) { throw new RuntimeException( "Login failed: " + loginResponse.code() + " " + loginResponse.body().string() ); } } return client; } public static void main(String[] args) throws Exception { OkHttpClient client = getAuthenticatedClient(); MediaType mediaType = MediaType.parse("application/json"); RequestBody body = RequestBody.create(mediaType, ""); Request request = new Request.Builder() .url("https://noon-api-gateway.noon.partners/content/v1/product/group/get") .method("POST", body) .addHeader("Content-Type", "application/json") .addHeader("Accept", "application/json") .build(); Response response = client.newCall(request).execute(); } } ``` ### nodejs ```javascript // Authentiction code is added below, to know more about authentication // visit: https://noon-docs.noonpartners.dev/docs/authentication/authenticating-requests import fs from 'fs'; import crypto from 'crypto' const credentials = JSON.parse( fs.readFileSync('noon_credentials_sensitive.json', 'utf8'), ); const BASE_URL = 'https://noon-api-gateway.noon.partners'; const USER_AGENT = 'NoonApiClient/1.0'; function base64url(value) { return Buffer.from(value).toString('base64url'); } function create_jwt() { const header = base64url(JSON.stringify({ alg: 'RS256', typ: 'JWT' })); const payload = base64url(JSON.stringify({ sub: credentials.key_id, // iat must be current Unix time in seconds; do not apply a timezone offset. iat: Math.floor(Date.now() / 1000), jti: crypto.randomUUID(), })); const signing_input = `${header}.${payload}`; const signature = crypto.sign( 'RSA-SHA256', Buffer.from(signing_input), credentials.private_key, ); return `${signing_input}.${base64url(signature)}`; } function get_cookie_header(response) { const set_cookie_headers = response.headers.getSetCookie?.() || [response.headers.get('set-cookie')].filter(Boolean); return set_cookie_headers .map((cookie) => cookie.split(';')[0]) .join('; '); } async function get_authenticated_session() { const response = await fetch(`${BASE_URL}/identity/public/v1/api/login`, { method: 'POST', headers: { 'User-Agent': USER_AGENT, 'Content-Type': 'application/json', }, body: JSON.stringify({ token: create_jwt(), default_project_code: credentials.project_code, }), }); if (response.status !== 200) { throw new Error(`Login failed with HTTP ${response.status}: ${await response.text()}`); } const cookie_header = get_cookie_header(response); return { get: (path) => fetch(`${BASE_URL}${path}`, { headers: { 'User-Agent': USER_AGENT, Cookie: cookie_header }, }), post: (path, body) => fetch(`${BASE_URL}${path}`, { method: 'POST', headers: { 'User-Agent': USER_AGENT, 'Content-Type': 'application/json', Cookie: cookie_header }, body: JSON.stringify(body), }), }; } const session = await get_authenticated_session(); const res = await session.post('/content/v1/product/group/get'); const data = await res.text(); ``` ### php ```php $credentials['key_id'], // iat must be current Unix time in seconds; do not apply a timezone offset. 'iat' => time(), 'jti' => Uuid::uuid4()->toString(), ], $credentials['private_key'], 'RS256'); } function get_authenticated_session(): Client { global $credentials; $client = new Client([ 'base_uri' => BASE_URL, 'cookies' => new CookieJar(), 'http_errors' => false, 'headers' => ['User-Agent' => USER_AGENT], ]); $response = $client->post('/identity/public/v1/api/login', [ 'json' => [ 'token' => create_jwt(), 'default_project_code' => $credentials['project_code'], ], ]); if ($response->getStatusCode() !== 200) { throw new Exception( 'Login failed with HTTP ' . $response->getStatusCode() . ': ' . $response->getBody() ); } return $client; } $session = get_authenticated_session(); $body = ''; $response = $session->post('/content/v1/product/group/get', ['json' => json_decode($body, true)]); $data = $response->getBody()->getContents(); ``` ### csharp ```csharp // Authentiction code is added below, to know more about authentication // visit: https://noon-docs.noonpartners.dev/docs/authentication/authenticating-requests using System.Net; using System.Net.Http.Json; using System.Security.Cryptography; using System.Text; using System.Text.Json; const string BASE_URL = "https://noon-api-gateway.noon.partners"; const string USER_AGENT = "NoonApiClient/1.0"; var credentials = JsonSerializer.Deserialize>( File.ReadAllText("noon_credentials_sensitive.json") )!; string base64url(byte[] value) => Convert.ToBase64String(value).TrimEnd('=').Replace('+', '-').Replace('/', '_'); string create_jwt() { var header = base64url(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new { alg = "RS256", typ = "JWT" }))); var payload = base64url(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new { sub = credentials["key_id"], // iat must be current Unix time in seconds; do not apply a timezone offset. iat = DateTimeOffset.UtcNow.ToUnixTimeSeconds(), jti = Guid.NewGuid().ToString() }))); var signingInput = $"{header}.{payload}"; using var rsa = RSA.Create(); rsa.ImportFromPem(credentials["private_key"]); var signature = rsa.SignData( Encoding.UTF8.GetBytes(signingInput), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1 ); return $"{signingInput}.{base64url(signature)}"; } async Task get_authenticated_session() { var handler = new HttpClientHandler { CookieContainer = new CookieContainer() }; var client = new HttpClient(handler) { BaseAddress = new Uri(BASE_URL) }; client.DefaultRequestHeaders.UserAgent.ParseAdd(USER_AGENT); var response = await client.PostAsJsonAsync("/identity/public/v1/api/login", new { token = create_jwt(), default_project_code = credentials["project_code"] }); if (response.StatusCode != HttpStatusCode.OK) { throw new Exception( $"Login failed with HTTP {(int)response.StatusCode}: {await response.Content.ReadAsStringAsync()}" ); } return client; } var session = await get_authenticated_session(); var content = new StringContent("", null, "application/json"); var response = await session.PostAsync("/content/v1/product/group/get", content); response.EnsureSuccessStatusCode(); Console.WriteLine(await response.Content.ReadAsStringAsync()); ``` ### curl ```bash # Authentiction code is added below, to know more about authentication # visit: https://noon-docs.noonpartners.dev/docs/authentication/authenticating-requests #!/usr/bin/env bash set -euo pipefail NOON_BASE_URL="${NOON_BASE_URL:-https://noon-api-gateway.noon.partners}" CREDENTIALS_FILE="${NOON_CREDENTIALS_FILE:-noon_credentials_sensitive.json}" NOON_USER_AGENT="${NOON_USER_AGENT:-NoonApiClient/1.0}" COOKIE_JAR="$(mktemp "${TMPDIR:-/tmp}/noon_cookies.XXXXXX")" PRIVATE_KEY_FILE="$(mktemp "${TMPDIR:-/tmp}/noon_private_key.XXXXXX")" trap 'rm -f "$COOKIE_JAR" "$PRIVATE_KEY_FILE"' EXIT cred() { jq -r --arg key "$1" '.[$key]' "$CREDENTIALS_FILE"; } b64url() { openssl base64 -A | tr '+/' '-_' | tr -d '='; } create_jwt() { cred private_key > "$PRIVATE_KEY_FILE" chmod 600 "$PRIVATE_KEY_FILE" header_b64="$(printf '%s' '{"alg":"RS256","typ":"JWT"}' | b64url)" # iat must be current Unix time in seconds; do not apply a timezone offset. payload_json="$( jq -cn \ --arg sub "$(cred key_id)" \ --argjson iat "$(date +%s)" \ --arg jti "$(uuidgen | tr '[:upper:]' '[:lower:]')" \ '{sub:$sub,iat:$iat,jti:$jti}' )" payload_b64="$(printf '%s' "$payload_json" | b64url)" signing_input="${header_b64}.${payload_b64}" signature_b64="$( printf '%s' "$signing_input" \ | openssl dgst -sha256 -sign "$PRIVATE_KEY_FILE" -binary \ | b64url )" printf '%s.%s\n' "$signing_input" "$signature_b64" } get_authenticated_session() { body="$( jq -cn \ --arg token "$(create_jwt)" \ --arg project_code "$(cred project_code)" \ '{token:$token,default_project_code:$project_code}' )" login_result="$( curl --silent --show-error \ --request POST "${NOON_BASE_URL}/identity/public/v1/api/login" \ --header "User-Agent: ${NOON_USER_AGENT}" \ --header "Content-Type: application/json" \ --cookie-jar "$COOKIE_JAR" \ --data "$body" \ --write-out $'\n%{http_code}' )" login_status="${login_result##*$'\n'}" login_body="${login_result%$'\n'*}" if [ "$login_status" != "200" ]; then echo "Login failed with HTTP $login_status:" >&2 echo "$login_body" >&2 exit 1 fi printf '%s\n' "$COOKIE_JAR" } SESSION_COOKIE_JAR="$(get_authenticated_session)" curl -L -X POST "${NOON_BASE_URL}/content/v1/product/group/get" \ --cookie "$SESSION_COOKIE_JAR" \ --header "User-Agent: ${NOON_USER_AGENT}" \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -d '' ```