Data structures%3B Commodity codes - uktrade/tariff-data-manual GitHub Wiki

Commodity codes

Commodity codes classify goods for import and export so traders can:

  • fill in declarations and other paperwork
  • check if there’s duty or VAT to pay
  • find out about duty reliefs

There are about 25,000 commodity codes in the UK Tariff. About 16,000 of the total number of commodity codes are declarable end-lines. An end-line is the most specific classification of a product.

Commodity codes contain 10 digits.

The first 6 digits are regulated by the World Customs Organization (WCO). This system is called the Harmonized System (HS). The system is used by more than 200 countries and economies as a basis for their Customs tariffs. Read more about the Harmonized System on the WCO website.

A revision takes place every 5 years. The most recent change took place in January 2022.

The last 4 digits are used by countries and trading blocks to subclassify goods, thus it is possible that the same code may refer to different goods depending on the country. For the purposes of goods classification, the UK continues to maintain regulatory alignment with the EU, hence the last 4 digits of codes in the UK are the same as those used in the EU.

What commodity codes are made up of

The full list of products in the UK Tariff is called the 'goods classification'. Sometimes it is also referred to as the 'goods nomenclature' or 'nomenclature'.

The UK goods classification is divided into 21 sections. These are shown in the Tariff as Roman numerals. Section numbers do not form part of a commodity code.

graph G {
  rankdir=LR
  node [shape=none]

  HS [label=<<TABLE CELLSPACING="0">
    <TR>
      <TD>1</TD>
      <TD>2</TD>
      <TD>3</TD>
      <TD>4</TD>
      <TD>5</TD>
      <TD>6</TD>
    </TR>
    <TR>
      <TD COLSPAN="2">HS chapter</TD>
      <TD COLSPAN="2">HS heading</TD>
      <TD COLSPAN="2">HS<BR/>subheading</TD>
    </TR>
    <TR>
      <TD COLSPAN="6">Managed by WCO</TD>
    </TR>
  </TABLE>>]

  CN [label=<<TABLE CELLSPACING="0">
    <TR>
      <TD>7</TD>
      <TD>8</TD>
      <TD>9</TD>
      <TD>10</TD>
    </TR>
    <TR>
      <TD COLSPAN="2">CN<BR/>subheading</TD>
      <TD COLSPAN="2">TARIC<BR/>subheading</TD>
    </TR>
    <TR>
      <TD COLSPAN="4">Managed by EU</TD>
    </TR>
  </TABLE>>]

  HS -- CN [color=none];
}

The first pair of numbers: the HS chapter

Within each section there are a series of chapters. The first pair of numbers in each commodity code shows which HS chapter of the Harmonized System it is in.

The second pair of numbers: the HS heading

Each HS chapter contains a series of headings. The second pair of numbers in each commodity code (digits 3 and 4) show which HS heading it comes under.

The third pair of numbers: the HS subheading

The third pair of numbers in each commodity code (digits 5 and 6) show which HS subheading it comes under.

The fourth pair of numbers: the CN subheading

The EU refers to its tariff as the combined nomenclature (CN).

The fourth pair of numbers in every commodity code (digits 7 and 8) show the CN subheading. The CN subheading is a further development of the Harmonized System. It's used to classify goods when they are declared to customs in the EU. This 8-digit nomenclature cannot be changed over a period of one year.

The fifth pair of numbers: TARIC codes

The fifth pair of numbers (digits 9 and 10) are known as TARIC codes.

TARIC is the tariff rate of the European Communities. The first 8 numbers are the same as the combined nomenclature. These 8-digit codes are broken down further into 10-digit codes.

If a commodity codes appears to have fewer than 10 characters

Commodity codes are sometimes shown with fewer than 10 characters. This is to represent a higher level in the classification hierarchy.

In these cases, the missing numbers are always zeroes.

For example, a commodity code written as 0102 is actually the 10-digit code 0102000000. Any spaces or dots added to commodity codes are for presentation purposes only.

Chapters 98 and 99 of the goods classification

Chapters 98 and 99 are part of the EU’s special coding system and are not defined by the WCO Harmonized System.

Chapter 98 is for statistical reporting purposes for industrial plant. No codes in this chapter have measures so traders cannot declare on them.

Chapter 99 contains codes for certain specific movements of goods. This includes things like personal effects belonging to a person moving house. Chapter 99 was optional for EU member states and the UK did not legislate for its use. It is not available for declarations but remains in the dataset.

Why the online tariff has indents

The Tariff originated as a printed document.

Indents were used to identify how many centimetres into the page the heading should appear.

This showed approximately how deep a commodity code was in the hierarchy.

The following image shows:

  • a hierarchy from the first commodity code in the Tariff down to the last end-line under the first major HS heading branch
  • a product line suffix for each commodity code
  • the indent for each commodity code
  • a description of the commodity code

The physical indent on the description matches the indent field. So, ‘Horses’ is a ‘child’ of ‘Live horses, asses, mules and hinnies’ and ‘Pure-bred breeding animals’ is a child of ‘Horses’.

The HS chapter and the HS heading use an indent of zero to show they are not indented.

Commodity Suffix Indents Description
0100 00 00 00 80 0 LIVE ANIMALS
0101 00 00 00 80 0 Live horses, asses mules and hinnies
0101 21 00 00 10 1     Horses
0101 21 00 00 80 2         Pure-bred breeding animals
0101 29 00 00 80 2         Other
0101 29 10 00 80 3             For slaughter
0101 29 90 00 80 3             Other
0101 30 00 00 80 1     Asses
0101 90 00 00 80 1     Other

Entity-relationship diagram for commodity codes and indents

The below E-R diagram shows the data structure of the central goods nomenclature entites and the indents entities.

Table "Goods Nomenclature" {
  "Goods Nomenclature Id" int [pk]
  "Commodity Code (Goods Nomenclature Item Id)" char(10)
  "Suffix" char(2)
  "Statistical Indicator" binary
  "Validity Start" date [not null]
  "Validity End" date
}

Table "Goods Nomenclature Indents" {
  "Indent Id" char(3) [pk]
  "Goods Nomenclature Id" int [pk]
  "Validity Start" date [not null]
  "Number of Indents" int
  "Commodity Code (Goods Nomenclature Item Id)" char(10)
  "Suffix" char(2)
}

Table "Goods Nomenclature Description Period" {
  "Description Period Id" char(3) [pk]
  "Goods Nomenclature Id" int [pk]
  "Commodity Code (Goods Nomenclature Item Id)" char(10)
  "Suffix" char(2)
  "Validity Start" date [not null]
  "Description" varchar(255)
}

Table "Goods Nomenclature Description" {
  "Description Period Id" char(3) [pk]
  "Goods Nomenclature Id" int [pk]
  "Commodity Code (Goods Nomenclature Item Id)" char(10)
  "Suffix" char(2)
  "Language Id " int
  "Description" varchar(255)
}

Ref: "Goods Nomenclature Indents"."Goods Nomenclature Id" > "Goods Nomenclature"."Goods Nomenclature Id"
Ref: "Goods Nomenclature Description Period"."Goods Nomenclature Id" > "Goods Nomenclature"."Goods Nomenclature Id"
Ref: "Goods Nomenclature Description"."Description Period Id" > "Goods Nomenclature Description Period"."Description Period Id"
Ref: "Goods Nomenclature Description"."Goods Nomenclature Id" > "Goods Nomenclature"."Goods Nomenclature Id"

Defining if a product is declarable: the product line suffix

A product line suffix is a separate pair of numbers shown beside the commodity code.

A value of 80 means the commodity code could in theory be declared. A value of less than 80, such as 10 or 20, means the commodity code is being used for grouping and structure purposes only. Measures can only be attached to codes with a suffix of 80.

A product is only declarable if both the following apply:

  • it has the product line suffix 80
  • it is an end-line (that is, it does not have any 'child codes' in the hierarchy)

In the following example, the declarable commodities are those with a D against them.

Although 0101 29 00 00 and 0101 00 00 00 have a product line suffix 80, they cannot be declared because they have child codes.

Commodity Suffix Indents Description Declarable
0100 00 00 00 80 0 LIVE ANIMALS
0101 00 00 00 80 0 Live horses, asses mules and hinnies
0101 21 00 00 10 1     Horses
0101 21 00 00 80 2         Pure-bred breeding animals D
0101 29 00 00 80 2         Other
0101 29 10 00 80 3             For slaughter D
0101 29 90 00 80 3             Other D
0101 30 00 00 80 1     Asses D
0101 90 00 00 80 1     Other D

The hierachical nature of commodity codes

Codes exist in a tree structure, with parent and child codes. The parent of "Pure-bred breeding animals" is "Horses" – so in fact, the commodity code 0101 21 00 00 only applies to horses that are pure-bred and for breeding. A pure-bred breeding bull would not fall under 0101 21 00 00. Hence, understanding the ancestors of a commodity code is important as well.

In the tables above, you can see 0101 21 00 00 listed twice – once for "Horses" and once for "Pure-bred breeding animals", and despite having the same code the former is the parent of the latter. This is because "Horses" is an intermediate line – it is not a legally recognised code, but exists just to provide more structure to the tree. This is where the "suffix" comes into play – a commodity code can exist multiple times with different suffixes, and only the code with the suffix marked "80" can be declared or referenced in legislation.

The parent of a commodity code at a given date is defined as the commodity code with:

  • An indent equal to 1 less (except for codes with indents of 0)
  • A code less than, or if equal a suffix less than
  • Validity dates on code and indent that contain the given date
  • A position latest in the commodity code list when sorting by code and suffix

So in the tables above, 0101 29 10 00 exists at indent 3, and hence is a child of 0101 29 00 00 at indent 2. And 0101 21 00 00 with suffix 10 is the parent of 0101 21 00 00 with suffix 80, because although the codes are the same the suffixes are different and 10 is less than 80.

Note that for legacy reasons, the top two levels of the hierarchy (so codes with eight trailing zeroes, and codes with 6 trailing zeroes) both have indents of 0 but still exist in parent/child relationship: 0100 00 00 00 is the parent of 0101 00 00 00; hence the 'indent equal to 1 less' requirement does not apply for these parent/child relationships.

We have develped an algorithm to generate a table listing the parent code for each commodity code, as shown in the table below. You can view the sql code behind the table in order to examine the process in more detail.

BEGIN TRANSACTION;

-- We create a mapping of CCs to the next CC with the same indent
-- that we will use later to inherit down down to CCs that don't
-- have their own measures. We do this using a window function by
-- ordering by CC and then selecting the next row with the same indent.
-- We do this as a temporary table rather than a CTE so that we can
-- put some indexes on it.
CREATE TEMPORARY TABLE ccs (
  cc char(10),
  sid int4,
  ptr_id integer,
  indent integer,
  next char(10),
  next_suffix char(2),
  next_sid int4,
  suffix char(2)
);

CREATE INDEX suffix_index ON ccs (suffix, cc);

CREATE INDEX cc_index ON ccs (cc);

CREATE INDEX next_index ON ccs (next);

CREATE INDEX sid_index ON ccs (sid);

CREATE INDEX next_sid_index ON ccs (next_sid);

WITH live_codes AS (
  SELECT
    *
  FROM
    commodities
  WHERE
    commodities.item_id LIKE '01%'
    AND (
      commodities.validity_end IS NULL
      OR date(commodities.validity_end) > date('2021-01-01')
    )
),
basic_indents AS (
  SELECT
    gni.sid AS indent_id,
    gni.indented_goods_nomenclature_id,
    gni.validity_start,
    MAX(gni.validity_start) OVER (
      PARTITION BY gni.indented_goods_nomenclature_id
      ORDER BY
        gni.validity_start ASC ROWS BETWEEN CURRENT ROW
        AND 1 FOLLOWING
    ) AS validity_end,
    (
      CASE
        WHEN gn.item_id LIKE '%00000000' THEN -1
        ELSE gni.indent
      END
    ) AS number_indents,
    gn.item_id,
    gn.suffix,
    gn.validity_start AS gn_start,
    gn.validity_end AS gn_end
  FROM
    commodity_indents AS gni
    LEFT OUTER JOIN commodities gn ON gni.indented_goods_nomenclature_id = gn.trackedmodel_ptr_id
),
goods_indents AS (
  SELECT
    indent_id,
    indented_goods_nomenclature_id,
    date(validity_start) AS validity_start,
    date(
      CASE
        WHEN basic_indents.validity_end = basic_indents.validity_start THEN NULL
        ELSE basic_indents.validity_end
      END
    ) AS validity_end,
    number_indents,
    indented_goods_nomenclature_id,
    suffix,
    gn_start,
    gn_end
  FROM
    basic_indents
),
parents AS (
  SELECT
    parents.*,
    (
      CASE
        WHEN parents.item_id LIKE '%00000000' THEN -1
        ELSE parent_indents.number_indents
      END
    ) AS number_indents
  FROM
    live_codes AS parents
    LEFT OUTER JOIN goods_indents AS parent_indents ON parents.trackedmodel_ptr_id = parent_indents.indented_goods_nomenclature_id
  WHERE
    date(parent_indents.validity_start) <= date('2021-01-01')
    AND (
      date(parent_indents.validity_end) >= date('2021-01-01')
      OR parent_indents.validity_end IS NULL
    )
)
INSERT INTO
  ccs
SELECT
  parents.item_id as cc,
  parents.sid AS sid,
  parents.trackedmodel_ptr_id AS ptr_id,
  parents.number_indents as indent,
  LEAD(parents.item_id) OVER (
    PARTITION BY parents.number_indents
    ORDER BY
      parents.item_id,
      parents.suffix
  ) AS next,
  LEAD(parents.suffix) OVER (
    PARTITION BY parents.number_indents
    ORDER BY
      parents.item_id,
      parents.suffix
  ) AS next_suffix,
  LEAD(parents.sid) OVER (
    PARTITION BY parents.number_indents
    ORDER BY
      parents.item_id
  ) AS next_sid,
  parents.suffix AS suffix
FROM
  parents;

-- Now we create the mapping of CCs to all descendent CCs.
-- We do this using the table above, selecting all of the CCs
-- that exist between the two limits and at a greater indent.
-- Again we do this as a temporary table so we can take advantage
-- of indexes.
CREATE TEMPORARY TABLE cc_children (
  parent_sid int4,
  parent_cc char(10),
  parent_ptr_id int4,
  parent_suffix char(2),
  parent_indent integer,
  child_sid int4,
  child_cc char(10),
  child_ptr_id int4,
  child_suffix char(2),
  child_indent integer
);

CREATE INDEX parent_sid_index ON cc_children (parent_sid);

CREATE INDEX child_sid_index ON cc_children (child_sid);

INSERT INTO
  cc_children
SELECT
  cc1.sid as parent_sid,
  cc1.cc as parent_cc,
  cc1.ptr_id as parent_ptr_id,
  cc1.suffix as parent_suffix,
  cc1.indent as parent_indent,
  cc2.sid as child_sid,
  cc2.cc as child_cc,
  cc2.ptr_id AS child_ptr_id,
  cc2.suffix as child_suffix,
  cc2.indent as child_indent
FROM
  ccs as cc1,
  ccs as cc2
WHERE
  cc2.cc >= cc1.cc
  AND (
    cc2.cc < cc1.next
    OR cc1.next IS NULL
  )
  AND cc2.indent = cc1.indent + 1;

-- Finally we use the mapping. We must also select the correct (i.e. latest) description.
WITH all_descs AS (
  SELECT
    gad.sid AS description_sid,
    gad.described_goods_nomenclature_id AS area_sid,
    gad.description,
    MAX(gad.validity_start) AS validity_start
  FROM 
    commodity_descriptions gad
  GROUP BY gad.described_goods_nomenclature_id
),
all_codes AS (
  SELECT
    child_sid AS sid,
	child_ptr_id AS ptr_id,
    parent_sid AS parent
  FROM
    cc_children
  UNION
  SELECT
    sid,
	ptr_id,
    NULL AS parent
  FROM
    ccs
  WHERE
    indent = -1
)
SELECT
  all_codes.sid AS sid,
  children.item_id AS commodity_code,
  children.suffix AS suffix,
  all_descs.description,
  date(children.validity_start) AS validity_start,
  date(children.validity_end) AS validity_end,
  all_codes.parent AS parent_sid,
  parents.item_id AS parent_code
FROM
  all_codes
  LEFT OUTER JOIN commodities children ON all_codes.sid = children.sid
  LEFT OUTER JOIN commodities parents ON all_codes.parent = parents.sid
  LEFT OUTER JOIN all_descs ON all_codes.ptr_id = all_descs.area_sid
ORDER BY
  children.item_id,
  children.suffix
LIMIT 25;

COMMIT;

DROP TABLE ccs;

DROP TABLE cc_children;

Note that the above code only applies to codes in HS chapter 01, and only shows the first 25 results, to remove these restrictions from the code, delete the 'commodities.item_id LIKE '01%'' condition at line 36 and the 'LIMIT 25' condition at line 226.

How new commodity codes are created

An origin defines the commodity code which a new commodity code was taken from.

When a commodity code is split in 2, the ‘origin’ is the parent commodity code.

It is mandatory that at least one origin is created for every new commodity code.

Absorbing old commodity codes

A successor defines the commodity code which an existing commodity code has been absorbed into.

It is not mandatory for a successor code to be created, though there is the option to create them.

Residual codes

When a commodity code is split into parts, a specific code and a residual code are created. The residual code is usually labelled 'other' in the hierarchy.

The following example shows commodity code 2001 90 70 00 has been split into 2 child codes:

  • the specific code is assigned 10 as the final 2 digits
  • the residual code is assigned 90 as the final 2 digits
Code Suffix Indents Description
2000 00 00 00 80 0 PREPARATIONS OF VEGETABLES, FRUIT, NUTS OR OTHER PARTS OF PLANTS
2001 00 00 00 80 1     Vegetables, fruit, nuts and other edible parts of plants, prepared or preserved by vinegar or acetic acid
2001 90 00 00 80 2         Other
2001 90 70 00 80 3             Sweet peppers
2001 90 70 10 80 4                 Not containing added sugar
2001 90 70 90 80 4                 Other

If more child codes are added to the parent as siblings to 10 and 90, these are assigned 20, 30, 40 as the final 2 digits while space remains.

If a code is split on the last digit, rather than the last 2 digits, it is usually split into 1 and 9.

There are more than 6,500 residual codes in the database.

Entity-relationship diagram for origins and successors

This E-R diagram details the relationships between the central goods nomenclature entity and the origin/successor entities

Table "Goods Nomenclature" {
  "Goods Nomenclature Id" int [pk]
  "Commodity Code (Goods Nomenclature Item Id)" char(10)
  "Suffix" char(2)
  "Statistical Indicator" binary
  "Validity Start" date [not null]
  "Validity End" date
}

Table "Goods Nomenclature Origin" {
  "Goods Nomenclature Id" int [pk]
  "Derived Commodity Code" char(10)
  "Derived Suffix" char(2)
  "Commodity Code" char(10)
  "Suffix" char(2)
}

Table "Goods Nomenclature Successor" {
  "Goods Nomenclature Id" int [pk]
  "Absorbed Commoddity Code" char(10)
  "Absorbed Suffix" char(2)
  "Commodity Code" char(10)
  "Suffix" char(2)
}

Ref: "Goods Nomenclature Origin"."Goods Nomenclature Id" > "Goods Nomenclature"."Goods Nomenclature Id"
Ref: "Goods Nomenclature Successor"."Goods Nomenclature Id" > "Goods Nomenclature"."Goods Nomenclature Id"

Entity-Relationship diagram of 'external' links

This E-R diagram details the relationships between the central goods nomenclature entity and other tariff data structure entities (namely measures and footnotes)

Table "Goods Nomenclature" {
  "Goods Nomenclature Id" int [pk]
  "Commodity Code (Goods Nomenclature Item Id)" char(10)
  "Suffix" char(2)
  "Statistical Indicator" binary
  "Validity Start" date [not null]
  "Validity End" date
}

Table "Measure" {
  "Measure Id" int [pk]
  "Goods Nomenclature Id" int [pk]
  "Goods Nomenclature Item Id" char(10)
  "..." "..."
}

Table "Footnote Association Goods Nomenclature" {
  "Footnote Id" int [pk]
  "Footnote Type"
  "Goods Nomenclature Id" int [pk]
  "Goods Nomenclature Item Id" char(10)
  "Suffix" char(2)
  "Validity Start" date [not null]
  "Validity End" date
}

Ref: "Measure"."Goods Nomenclature Id" > "Goods Nomenclature"."Goods Nomenclature Id"
Ref: "Footnote Association Goods Nomenclature"."Footnote Id" > "Goods Nomenclature"."Goods Nomenclature Id"

Validation rules

Code Description
NIG1 Validity overlaps with self rule stating the validity periods must not overlap between goods with the same goods nomenclature id.
NIG10 Successor codes must become applicable the day after the end of the old code.
NIG11 Mandatory subrecord rule that each goods nomenclature id must have at least one indent record. The start date of the first indentation must be equal to the start date of the nomenclature. No two associated indentations may have the same start date. The start date must be less than or equal to the end date of the nomenclature.
NIG12 Mandatory subrecord rule for goods nomenclature descriptions. The start date of the first description period must be equal to the start date of the nomenclature. No two associated description periods may have the same start date. The start date must be less than or equal to the end date of the nomenclature.
NIG18 Footnotes with a footnote type for which the application type = “CN footnotes” must be linked to CN lines (all codes up to 8 digits).
NIG2 Validity contained rule requiring the validity period of the goods nomenclature to be within the validity period of the product line above in the hierarchy.
NIG22 Validity contained rule requiring the period of the association with a footnote to be within the validity period of the nomenclature.
NIG23 Validity contained rule requiring the period of the association with a footnote to be within the validity period of the footnote.
NIG24 Validity excluded rule that when the same footnote is associated more than once with the same nomenclature then there may be no overlap in their association periods.
NIG30 Validity contained rule requiring the validity period of a goods measure to be within the validity period of the goods nomenclature that it references.
NIG31 Validity contained rule requiring the validity period of a goods measure to be within the validity period of the goods nomenclature that it references.
NIG34 Deletion while in use rule for goods nomenclature used in goods measures.
NIG35 Deletion while in use rule for goods nomenclature used in additional nomenclature measures.
NIG5 Must exist rule that when creating a goods nomenclature code, an origin must exist. This rule is only applicable to update extractions. Almost all goods nomenclatures must have an origin, excluding two scenarios: they are a top level code (depth/indent of 1), they were made before 2010-01-01 (legacy data). Therefore check for these two conditions, and if neither are met ensure an origin exists.
NIG7 The origin must be applicable the day before the start date of the new code entered.

For more detail see the system documentation.

⚠️ **GitHub.com Fallback** ⚠️