Basic information

The basic principle of the API is very simple. GTIN (EAN) is the unique key that's used to look up products.

You send in a list of the GTINs of the products you want to retrieve information or media from, and we return the data for any matching products.


Access to the API

We have a limited trial account you can use to test the API with in the beginning. See the Trial section.

After testing the API, you’ll need to set up your own account. To do this, please contact us and provide some basic information about yourself and your intended use of the service. Following this, you’ll need to sign a contract that outlines the terms of service and usage policies.

Once the contract is signed, you’ll receive your account details, typically on the same day. In some rare instances, a specialized API may be required. If so, please reach out to us for further details.

There are multiple versions of this API, with the latest live version being 1.84, also known as v184.


Available information

The standard API offers two primary methods for retrieving product data:

  1. GetProductData: This method returns a basic collection of product information suitable for most applications.
  2. GetCustomProductData: This method allows you to specify the information you need, providing access to a much larger set of data.

The GetProductData method returns an object containing:

   
    GTIN
    Supplier article number
    Image URL
    Supplier name
    Brand name
    Product name
    Weight / volume
    Content declaration
    Nutrition
    Product markings
    Allergy information
    Custom Fields


The information you can choose from in GetCustomProductData, is:

   Images (Sent in the form of encrypted URLs. You can ask for images in multiple sizes/formats in each request)
   Product name
   Supplier name
   Brand name
   Content declaration
   Nutrition
   Weight / volume
   Product markings (currently about 35 possible markings, like Svanen, Särnär glutenfri, Nyckelhålet, Krav etc.)
   About 20 different article numbers (For example GTIN, DUN, Supplier article number, ICA-number, COOP-number, Servera-number etc.)
   Width
   Height
   Depth
   Allergy information
   Durability
   Recycling
   Storage instructions
   Product description
   Sale text
   Category tree connections (for suppliers)
   Custom Fields
   NutritionPerServing
   OrderableLevels


To optimize data lookup speed, request only the information you need.


SOAP / JSON / SSL

Our API supports both JSON and SOAP requests, responding in the same format as the request. For example, a JSON request will receive a JSON response.

SSL (https) is mandatory. Any request made to http will be redirected to https.

Methods

   StringResult Get24hSecurityKey(string username, string password)
   ProductResult GetProductData(string[] gtinVec, ImageThumbFormat thumbFormat, ImageThumbSize thumbSize, string langTargetmarketLimit, string secKey)
   StringResult GetCustomProductDataFieldNames(string secKey)
   ArbitraryProductResult GetCustomProductData(string[] gtinVec, string[] requestedDataFields, string langTargetmarketLimit, string secKey)
   StringResult GetAllAvailableGTINs(string langTargetmarketLimit, string secKey)
   ChangedProductGTINResult GetChangedProductsGTINs(DateTime changedAfter, string secKey)



Return types

The API will always return an object containing three variables:

   XXXResult {
       bool Success;
       string ErrorMessage;
       XXX Data;
   }

Success tells you if the call was successful. If Success == false, ErrorMessage will contain a description of the error that has occurred. Data is a strongly typed variable containing hte requested data (if Success == true).

For a more detailed description of the return types, see the "Return types" chapter


Authentication

Get24hSecurityKey checks your user credentials and returns a security key valid for 24 hours. This security key should be cached in your solution and sent in evey subsequent request you make to the API.

Every time the method is called, a new key is generated.

To prevent brute force attacks and keep the logs somewhat readable, there is a limit to 50 security keys per user every 24 hours. So the recieved key needs to be cached and reused.


Available GTINs

GetAllAvailableGTINs returns a string vector with all GTINs you have access to.


Changed/Deleted products

For us and the suppliers it's of the utmost importance that all information from us, that you show to your users, is correct and updated.

GetChangedProductsGTINs returns a list of GTINs of the products that has been changed or deleted since the date/time you supply the metod with, and what kind of change it is (New/changed or deleted).

If the data is cached or stored in a database on your side, it's vital that this method is called at regular intervals to keep the data updated and accurate.

As soon as a product is marked as updated (TypeOfChange.Updated), its product data and images must be downloaded again.

If it's marked as deleted (TypeOfChange.Deleted), any product information or images you've recieved from us must be deleted.


GTIN

You supply the GetProductData and GetCustomProductData methods with a list of GTINs belonging to the products you want information about.

Only GTIN containing 8 or 13 digits are considered valid. Every sent in GTIN results in an object being returned, containing the requested data, in the same order that the GTINs were sent in. Every object contains a MatchFound property, indicating if a matching product could be found in our system or not.

To keep the responsetimes relatively low, it's not recommended to look up more that 500 GTINs in each request.


Two different methods for requesting product information


GetProductData

A simple method that you supply lith a list of the GTINs you want to look up and the type of image you want,and a product object is returned conatining a basic set of product data that would be sufficient in most situations:

   GTIN
   Supplier article number
   Image URL
   Supplier name
   Brand name
   Product name
   Weight / volume
   Content declaration
   Nutrition
   Product markings
   Allergy information
   Custom Fields


GetCustomProductData

This method gives you the ability to customize and pick and choose exactly what information you want in each call. It gives you the ability to choose from a much wider array of data, and the ability to request for instance all images of the product (not just the main image) and even those images in several different sizes/formats in each request.

To get the "names" of the available fields, you call GetCustomProductDataFieldNames.

Images

Images can be downloaded in three different formats:

   Jpeg
   Png, 24 bit (with transparency)
   Tiff

and in several different sizes:

   50 px
   75 px
   100 px
   150 px
   200 px
   250 px
   300 px
   400 px
   500 px
   750 px
   1000 px *
   1500 px *
   2000 px *
   Original size *
   * These sizes are disabled by default, but can be unlocked for your account by contacting us


GetCustomProductData

You supply GetCustomProductData with a string detailing what kind of image you want, from a number of predefined options. It's structured like "Image_Format_Size". Format is entered like Jpg, Png or Tif. The size entered like px100, px200, px500. So Image_Png_px400 would give you a link to the main product image in Png-format, 400 pixels wide and/or high. Image_Jpg_px100 would give you a link to an image thats 100 pixels wide and/or high in Jpeg format.

It's be possible to request for instance both "Image_Png_px75" and "Image_Png_px300" in GetCustomProductData, and recieve links to both images in every request.


Image & AllImages

In GetCustomProductData there are two ways to request images. Image returns a link to the primary product image.

But a product can have an unlimited number of images attached. That's why AllImages exists

If you request AllImages_Jpg_px200, it would return all images belonging to the product, with the URL to the image and some additional data about it (for example the type of image, like ProductImage or AssortmentImage), and information about angle, which side is visible primarily and so on. The additional data conatins all the information necessary for creating a GS1 file name, if that's something you need.

More information can be found in the class and structure listings at the end of the document.

Category tree

Suppliers can create category trees in OPV Online, and attach products to the different categories. The API has a number of methods that allows suppliers to retrieve these trees and its attached products. Before you can collect the category tree, you need to contact us in order for you to get your MenuId, required by these functions.

Every product can be attached to any number of categories. Every category han have an infinite number of sub-categories, in an infinite depth.

   MenuItemResult GetMenuTree(string language, string secKey)
   MenuItemResult GetMenuSubItems(long parentMenuItemId, string language, string secKey)
   ArbitraryProductResult GetMenuCustomProductData(long menuItemId, string[] requestedDataFields, string langTargetmarketLimit, string secKey)


GetMenuTree returns the entire tree at once.

GetMenuSubItems allows you to traverse the tree, by returning all the sub-categories of a certain category.

GetMenuCustomProductData works just like GetCustomProductData, but returns the product data of all products under a certain category instead of doing i GTIN lookup.


Custom Fields

Suppliers often require specific data relevant to their business, which may not be necessary for others. For example, a cheese manufacturer might need information on where and how long the cheese has been stored, and its taste intensity—details that are irrelevant to most other suppliers.
Custom Fields address this issue by allowing suppliers to add their own fields of various types (such as textboxes, radio buttons, drop-down menus, etc.) with custom rules and validations. This enables them to enter additional product data not covered by the standard fields.

CustomFields returns all available custom fields for the current product in a simple Name-Value type of structure.

Old products might not have any custom-fields available or even only have a subset of the total set available for the company, so a Custom Field that exists on one product, might be empty or null on another.

EU 1169/2011

By the end of 2014 EU regulation 1169/2011 became active. This requires anyone selling for instance food, no matter if it's online or in a store, to give the customer access to some basic information:

1 a) The name of the food

1 b) The list of ingredients

1 c) Any ingredient or processing aid listed in Annex II or derived from a substance or product listed in Annex II causing allergies or intolerances used in the manufacture or preparation of a food and still present in the finished product, even if in an altered form.

1 d) The quantity of certain ingredients or categories of ingredients.

1 e) The net quantity of the food

1 f) The date of minimum durability or the ‘use by’ date (Not required in online stores)

1 g) Any special storage conditions and/or conditions of use

1 h) The name or business name and address of the food business operator referred to in Article 8(1)

1 i) The country of origin or place of provenance where provided for in Article 26

1 j) Instructions for use where it would be difficult to make appropriate use of the food in the absence of such instructions

1 k) With respect to beverages containing more than 1,2 % by volume of alcohol, the actual alcoholic strength by volume

1 l) A nutrition declaration


All this information can be retireved through our API by calling GetCustomProductData and requesting:

SupplierName

TrademarkName

ProductName

WeightVolume (Net weight / volume)

Content

Nutrition

CompanyAddress

Storage (Instructions for storing the product)

Manual (Instructions for how to use the product)

Origin

PercentageOfAlcoholByVolume


[allergen]

Another requirement in EU 1169/2011 is that allergens are marked in bold style in all content declarations. So in many content declarations you'll see [allergen][/allergen]-tags. When writing out the content declaration, you need to do a replace to translate the [allergen] tags to a suitably styled HTML element.

Example:

Water, [allergen]flour[/allergen], yeast, [allergen]milk powder[/allergen], salt

needs to be transformed into

Water, flour, yeast, milk powder, salt

Trial

The easiest way to get familiar with how the APi works, is by calling it. So we've created a trial account that can be freely used to request data from a limited number of products.

The latest version of the API can be found here:

https://api.opv.se/WS/General/v184/ProductService.asmx


The user credentials to use with the Get24hSecurityKey-function is:

User name: WebService_TrialAccount

Password: NtcNOMZ8z0?_f2


It's the production service you're using, but with a limited account. So once you get the real user account, you only need to swtich the username and password, and you'll get access to alot more data.

We've developed a simple software that you can use to test the API: https://api.opv.se/WS/General/v184/WebServiceTester.zip The file conatins both the source code (in C#), and the runnable version (in the bin\release-folder).

To get a list of what GTINs you have access to, run the program and press the "Get all GTINs" button.

Calling the API

SOAP

The API describes how to call it using SOAP through the WSDL specification. Go to the API in an ordinary web browser, and click on the different methods, and you'll get a specification of how to call it and what data will be returned.

JSON

You call the different methods of the API through: https://api.opv.se/WS/General/v184/ProductService.asmx/{function_name}

For example:

https://api.opv.se/WS/General/v184/ProductService.asmx?op=Get24hSecurityKey

In the call, set dataType to "json" and contentType to "application/json; charset=utf-8".

As data, send in a JSON encoded object. For example {"username": "MyUsername", "password": "MyPassword"}


JSON code example:

   $.ajax(
   {
           type: "POST",
           dataType: "json",
           contentType: 'application/json; charset=utf-8',
           url: "https://api.opv.se/WS/General/v184/ProductService.asmx/GetCustomProductData",
           data: "{ 'gtinVec': [ '5701211106172', '6411200106685' ], 
                    'requestedDataFields': [ 'SupplierName', 'TrademarkName', 'ProductName', 'WeightVolume', 'Image_Jpg_px200' ], 
                    'langTargetmarketLimit': 'sv-SE', 'secKey': '"+ secKey +"' }",
           success: function (r) {
           if (r == null || r.d == null) {
               alert('Unknown error');
           }
           else if (!r.d.Success) {
               alert(r.d.ErrorMessage);
           }
           else {
               var data = r.d;
                // Start processing the data
           }
       }
   });

Note: In this example Javascript is used with jQuery to illustrate a call to the API. But because of cross site scripting-limitations, this won't work in reality. But most languages would do this in a similar way.

If you want load data from the API with Javascript, you need to create a "proxy" in the backend that passes on the call to our API and returns the data to the frontend.

Call data

Fields that can be requested through GetCustomProductData

Field Datatype Information
AllergyInfo AllergyInfo[] Returns information about allergens in the product.

Contains 0: No, 1: Trace amounts, 2: Yes.

Note that allergens will me marked in the content declaration. That's where the customer should look first. These tables is to make allergens searchable, and not eveyone fills in this data.

AssortmentName String The assortment that the product belongs according to our own classification.
AxfoodNr String The Axfood article number.
AxfoodSnabbgrossNo String The Axfood Snabbgross -article number.
BergendahlNr String The Bergendahl article number.
Brand BrandInfo A BrandInfo-object, bontaining the name and our internal id of the brand.
ChangedDate DateTime The date when the products data was last changed.
Company Company The address information of the supplier (GLN, and in CompanyAddress: Name, Street address, Zip code and City).
Content String The content declaration of the product

Contains [allergen][/allergen]-taggs around allergens. These needs to be replaced by for instance <strong>-tags to make them bold and easy for the customer to find.

In the future the [allergen]-tags might contain attributes that would tell what kind of allergen it is (for example [allergen allergenId="5" allergenName="Milk"]), so it's recommended to use regular expressions when doing the replace. Something along the lines of RegEx.Replace(content, "\[allergen.*?\]", "<span class="bold" >");

Cooking String Cooking instructions.
COOPNr String The COOP article number.
CustomFields CustomFieldInfo[] Suppliers can add their own custom fields if they feel something is missing, to extend the number of available data fields and fill in data relevant for their own users or systems. "CustomFields" returns all available custom fields for the current product. Old products might not have any custom-fields available or even only have a subset of the total set available for the company, so a Custom Field that exists on one product, might be empty or null on another.
DafgardNo String The Dafgård article number.
Depth Double The depth of the product in mm.
Description String Product description.
Durability String Information about the durability of the product (like "8 days in the refridgerator").
GTIN String The GTIN number of the product. Might be 8, 13 or 14 digits.
Height Double The height of the product in mm.
ICANr String The ICA article number.
LaunchDate DateTime The date when the product was launched.
Manual String Instructions of how to use the product.
Markings MarkInfo[] Official product markings, like Svanen, Nyckelhålet, Fairtrade etc.

1 Bra Miljöval/Falken
2 Svanen
3 KRAV-märkt
4 Nyckelhålet
5 Särnär naturligt glutenfri
6 Särnär fri från Gluten
7 Särnär fri från Laktos
8 Särnär Laktosreducerad
9 Särnär fri från Soja
10 Särnär Ärtproteinfri
11 Särnär fri från Mjölkprotein
12 Särnär fri från Ägg
13 Sockerfri
14 Särnär
15 Osötat
16 Svenskt sigill
17 Organic
18 EU-blomman
19 Särnär fri från Jordnötter
21 Särnär fri från Fisk
22 Särnär fri från Nöt/Mandel
23 Fairtrade
24 GMO märkt
25 Godk. av Astma & Allergif.
26 Särnär fri från Mjölk
27 Särnär proteinreducerad
28 Särnär laktasenzym
29 Särnär proteinfri
30 Särnär fenylalaninfattig
31 EU-logotype för ekologiska produkter
32 MSC-märkt fisk
33 Steril
34 Höggradigt ren
36 Svensk fågel
37 Smakstyrka 1
38 Smakstyrka 2
39 Smakstyrka 3
40 Smakstyrka 4
41 Smakstyrka 5
42 Smakstyrka 6
43 Smakstyrka 7
44 Smakstyrka 8
45 Smakstyrka 9
46 Smakstyrka 10
47 Smakstyrka 11
48 UTZ-Certified
49 Rainforest Alliance
50 Animal Welfare Approved
51 FSC
52 Svenskt kött
53 Utan tillsatt socker
54 Lågt sockerinnehåll
55 Äkta vara
56 Halal *
57 Kosher *
58 Vegan *
59 Vegetarisk *
60 Fritt från nötkött *
61 Fritt från fläskkött *
62 Hjärtat
63 Från Sverige
65 Svenskt sigill klimatcertifierad
67 ASC Certified
68 Vegecert
69 European Vegetarian Union *
70 Kött från Sverige

* Diet type

MartinAndServeraNo
MartinServeraNo
Martin&ServeraNo
String The Martin and Servera article number.
MenigoNo String The Menigo article number.
MenuConnections MenuItem[] For suppliers that use the category tree function. Returns a list of the categories the product is attached to
NordisktVaruNo String The Nordiskt Varu -article number.
Nutrition String The nutritional declaration of the product. This is always per 100 g/ml (measured).
NutritionSeparated Array of NutrientQuantitySeparated The nutritional declaration of the product, separated so that each row is returned separated into name, amount and unit
First each row is devided into a "Nutrient" and a "Quantity" ("Protein 12,7g" is divided into Nutrient: "Protein", Quantity: "12,7g"). These values are suitable if you just want to separate them into a table.
But Quantity is also separated into amount and unit, that can be acessed through the "QuantitySeparated" property. In this example it would mean that you get:
{ Nutrient: "Protein", Quantity: "12,7g", QuantitySeparated: { Amount: 12.7, Unit: "g" } }
NutritionPerServing NutritionPerServing The nutritional declaration of the product per serving. The amount per serving varies. Returns an object containing: Nutrition (String), ServingSizeGram (Integer), Description (new!) (String), and ReferenceRecommendation (String). ReferenceRecommendation is a freetextfield that might be empty, or contain a string hinting of what type of "recommended" nutrient intake it is about, if this information is available. Maximum one NutrientPerServing per product is available.
NutritionPerServingSeparated Array of NutrientQuantitySeparated The nutritional declaration per serving of the product, separated so that each row is returned separated into name, amount and unit
First each row is devided into a "Nutrient" and a "Quantity" ("Protein 12,7g" is divided into Nutrient: "Protein", Quantity: "12,7g"). These values are suitable if you just want to separate them into a table.
But Quantity is also separated into amount and unit, that can be acessed through the "QuantitySeparated" property. In this example it would mean that you get:
{ Nutrient: "Protein", Quantity: "12,7g", QuantitySeparated: { Amount: 12.7, Unit: "g" } }
OrderableLevels Array of OrderableLevel Array of orderable levels (often cases), which contain this GTIN/EAN. Each orderable level has GTIN, a supplier assigned number and number of units. There might be multiple orderable levels. Not all products or suppliers do have this information stored, and if no information is found the array will be empty/null.
Origin String Information about the origin of the product.
PercentageOfAlcoholByVolume String The alcohol contant of the product by volume percent.
ProductId Long The internal id we've assigned to the product. This might change when a product is updated.
PrivabNo String The PrivabNo article number.
ProductName String The name of the product.
Recycling String Instructions for recycling.
Saletext String The suppliers sale text.
Storage String Instructions for storing the product.
SuppArtNr String The suppliers own article number for the product.
SupplierName String The name of the supplier (can also be retrieved through CompanyAddress).
SvenskCaterNo String The Svensk Cater -article number.
SvenskSnabbmatNo String The Svensk snabbmat article number.
BrandName
TrademarkName
String The brand name of the product.
VivLogNo String The VivLog article number.
WeightVolume String The weight / volume of the product.
Width Double The width of the product in mm.
ÖoBNo
OoBNo
String The ÖoB (Överskottsbolaget) article number.
Image_[Format]_[Size] String A link to the primary image in the requested format and size

See the Images section for more information.

AllImages_[Format]_[Size] OPVImage[] Returns all images attached to the product with some additional data about each image.

See the Images section for more information.

Return types.

Result classes

   class OperationResult
   {
       bool Success
       string ErrorMessage
   }
   class ProductResult : OperationResult
   {
       Product[] Data
   }
   class ArbitraryProductResult : OperationResult
   {
       ArbitraryProduct[] Data
   }
   class ChangedProductGTINResult : OperationResult
   {
       ChangedProductGTIN[] Data
   }
    class StringResult : OperationResult
   {
       string Data
   }
    class StringVecResult : OperationResult
   {
        string[] Data;
   }
   public class MenuItemResult : OperationResult
   {
       MenuItem[] Data
   }


Classes in the result Data property of the Result classes

    class Product
   {
       bool MatchFound      // If a matching GTIN could be found or not
       string GTIN
       string ImageURL
       string Supplier
       string Trademark
       string Name
       string WeightVolume
       string SuppArtNo
       string Content
       string Nutrition
       MarkInfo[] Markings
       AllergyInfo[] Allergies
       Company Company
   }
   class ArbitraryProduct
   {
       bool MatchFound      // If a matching GTIN could be found or not
       string GTIN
       NameValue[] Data
   class NameValue
    {
        string Name      // The requested column name
        object Value     // The value. Might be of varying data types
    }

   class ChangedProductGTIN
   {
       public string GTIN;
       public ChangeType TypeOfChange;
   }
   enum ChangeType
   {
       Updated,    // The information and images should be downloded anew, and replace the existing data
       Deleted     // The information and images retrieved fron us should be deleted in your system
   }

ArbitraryProduct.Data.Value might be of different data types. If not shown below, it will be a string:

   productid            long
   allergyinfo          AllergyInfo[]
   markings             MarkInfo[]
   width                double
   height               double
   depth                double
   launchdate           nullable DateTime
   changeddate          DateTime
   company              Company
   AllImages            OPVImage[]
   NutritionSeparated   NutrientQuantitySeparated[]
   CustomFields         CustomFieldInfo[]
   class AllergyInfo
   {
       int SubstanceId
       string SubstanceName           // Name of the substance, for instance "Gluten" or "Lupin"
       ContainsAllergenic Contains    // An enum indicating wheter it contains the allergen or not. No = 0, Possible = 1, Yes = 2. Possible is what often is rererred to as "Trace amounts"
       string Remark                  // Remark from the supplier for further information
   }
   class MarkInfo
   {
       int MarkId                     // Our internat id of the mark
       string MarkName                // The name of the mark
   }
   class CompanyAddress
   {
       string Name
       string Address
       string Zip
       string City
   }
   class NutritionPerServing
    {
        string Nutrition
        int ServingSizeGram
        string ReferenceRecommendation
    }

   class Company
   {
       string GLN
       CompanyAddress CompanyAddress  
   }
   class CustomFieldInfo
   {
       string Name            // The name of the custom field             
       string Value           // The value of the custom field
   }
   class NutrientQuantitySeparated
   {
       string Nutrient
       string Quantity
       string Comment
       AmountAndUnit SeparatedQuantity
   }

   class AmountAndUnit
   {
       double Amount
       string Unit
   }

    
   class BrandInfo
   {
        long BrandId
        string BrandName
   }

   class OrderableLevel
    {
        string GTIN
        string SupplierAssignedNumber
        int NumberOfUnits
    }

Classes returned when requesting AllImages from GetCustomProductData.

The letters within the parenthesis for the GDSN classes indicates the character it should be replaced with when generating a GDSN file name according to the 2013 standard

   class OPVImage
   {
       public string OPVNo = "";            // our internal id of the iamge. Probably not somehing that's of use to you
       public DBImageType ImagePurpose;     // Type of image. For example Product image, environment image, assortment image etc.
       public string ImageURL = "";         // The URL of the image
       public GDSNImageType? ImageType = null;   // Type of image according to GDSN
       public GDSNImageFacing? Facing = null;    // What primary side is visible
       public GDSNImageAngle? Angle = null;      // If it's taken from the front, or from a left or right angle
       public GDSNInOutPackage? InOutPackage = null;   // If it's photographed inside or out of its packaging
   }
   enum short DBImageType {
       ProductImage
       SpaceImage_front
       SpaceImage_top
       AssortmentImage
       EnvironmentImage
       SpaceImage_DF
       Video
       Audio
       Document
       SpaceImage_Side
   }
   enum GDSNImageType {
       StillShotSingleGTIN (A)
       StillShotSingleGTINWithElements (B)
       StillShotSingleGTINHiRes (C)
       StillShotSingleGTINWithElementsHiRes (D)
       Undetermined (Z)
   }
   enum GDSNImageFacing {
       NotApplicable (0)
       Front (1)
       Left (2)
       Top (3)
       Back (7)
       Right (8)
       Bottom (9)
   }
   enum GDSNImageAngle {
       Center (C)
       Left (L)
       Right (R)
       NoPlunge (N)
   }
   enum GDSNInOutPackge {
       OutOfpackaging (0)
       InPackaging (1)
       Case_ (A)
       Innerpack (B)
       RawOrUncooked (C)
       Prepared (D)
       Plated (E)
       Styled (F)
       Staged (G)
       Held (H)
       Worn (J)
       Used (K)
       Family (L)
       OpenCase (M)
   }

MenuItemResults Every MenuItem has three properties. A MenuItemId that's unique for each category in the tree (used when calling GetMenuCustomProductData), a Name-property containing the name of the category, and a Children-property of the type MenuItem[] containing all of its child categories.

   class MenuItem {
       long MenuItemId
       string Name
       long ParentId
       MenuItem[] Children
   }

Note that Children always is null when calling GetMenuSubItems, since that function only fetches one level at the time.