Feed APIFeed API

REST API / Categories

Categories

The platform-wide product taxonomy. Every product on Feed API is categorised against this one tree, so the same category means the same thing whichever supplier a product came from. Not scoped to your catalogue — use it to build navigation or filtering in your own storefront.

Note

The tree is the Shopify Product Taxonomy, version 2026-05: 14,606 categories under 26 verticals, up to 8 levels deep. It is read-only over the API, and identical for every reseller and supplier — a category id means the same thing in your storefront as it does in the supplier's catalogue.

Find a category

Search the taxonomy and copy the breadcrumb exactly as the importer expects it. Pasting the copied value into the Category column of a product CSV is the one way to name a category that can't be misread — every string this returns is byte-identical to what the importer matches against.

Category finder14,606 categories

Endpoint

GET/v1/categories

List categories

Returns the full category tree, nested under their parents. The tree is large — 14,606 categories across 26 top-level verticals, up to 8 levels deep — so treat this as a bulk reference you fetch and cache, not something you call on every page view.

Response

200 OK
{
  "data": [
    {
      "id": "2ab8df9b-b3ea-4977-ac42-af8c1fa189e3",
      "name": "Apparel & Accessories",
      "slug": "apparel-accessories-aa",
      "description": null,
      "children": [
        {
          "id": "dd943db3-a583-453a-acdb-0f80d4f3aa5d",
          "name": "Clothing",
          "slug": "clothing-aa-1",
          "description": null,
          "children": [
            {
              "id": "638ce987-1534-4a42-bfa8-9778eb9d5870",
              "name": "Pants",
              "slug": "pants-aa-1-12",
              "description": null,
              "children": [
                {
                  "id": "1e14381d-8b7c-4b12-b117-d57d50bf9de6",
                  "name": "Joggers",
                  "slug": "joggers-aa-1-12-7",
                  "description": null,
                  "children": []
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

That example is one branch, truncated for readability. Clothing alone has 425 categories under it.

Identifying a category

A category has several identifiers and they are not interchangeable. Three of the four below are unique across all 14,606 categories. name is not, and that is the single most common mistake made against this API.

idUniquefc78e0cc-4921-47b2-8718-b2c89264abcc
The value to store against your own products. Stable — it does not change when the taxonomy is updated.
slugUniquet-shirts-aa-1-13-8
URLs in your own storefront. Also carries the Shopify taxonomy code as its suffix — see Deriving the breadcrumb below.
breadcrumbUniqueApparel & Accessories > Clothing > Clothing Tops > T-Shirts
Anything a human reads, and the value a supplier's CSV import matches on. Derived from the tree, not a field on the response.
nameNot uniqueT-Shirts
Display only, always alongside its parent. Never use it to match or look up a category.

description is on the response but is null for every category in the taxonomy — don't build UI that depends on it.

Warning

205 names are shared by more than one category, covering 464 categories in total. Five different categories are called exactly T-Shirts:
Apparel & Accessories > Clothing > Activewear > Activewear Tops > T-Shirts
Apparel & Accessories > Clothing > Clothing Tops > T-Shirts
Apparel & Accessories > Clothing > Baby & Children's Clothing > Baby & Children's Tops > T-Shirts
Apparel & Accessories > Clothing > Maternity Clothing > Maternity Tops > T-Shirts
Arts & Entertainment > Hobbies & Creative Arts > Collectibles > Sports Collectibles > Sports Fan Accessories > T-Shirts

Match on id or on the full breadcrumb. A lookup by name will silently pick the wrong one.

Deriving the breadcrumb and the Shopify code

The response gives you id, name, slug, description and children. The breadcrumb, the depth and the Shopify taxonomy gid are not fields on it — but you don't need them to be, because all three fall out of one walk of the tree. Build this index once, cache it, and look categories up by id from then on.

Build a category index
type Category = {
  id: string;
  name: string;
  slug: string;
  description: string | null;
  children: Category[];
};

const res = await fetch("https://api.feedapi.co.uk/v1/categories", {
  headers: { "x-api-key": process.env.FEEDAPI_KEY! },
});
const { data }: { data: Category[] } = await res.json();

const index = new Map<
  string,
  { breadcrumb: string; depth: number; shopifyGid: string }
>();

function walk(nodes: Category[], trail: string[]) {
  for (const node of nodes) {
    const path = [...trail, node.name];

    // Every slug ends with the category's Shopify taxonomy code:
    // "t-shirts-aa-1-13-8" -> "aa-1-13-8". The number of segments in
    // that code is the category's depth.
    const code = node.slug.match(/([a-z]{2}(?:-\d+)*)$/)?.[1] ?? "";

    index.set(node.id, {
      breadcrumb: path.join(" > "),
      depth: path.length - 1,
      shopifyGid: `gid://shopify/TaxonomyCategory/${code}`,
    });

    walk(node.children, path);
  }
}

walk(data, []);

index.get("fc78e0cc-4921-47b2-8718-b2c89264abcc");
// {
//   breadcrumb: "Apparel & Accessories > Clothing > Clothing Tops > T-Shirts",
//   depth: 3,
//   shopifyGid: "gid://shopify/TaxonomyCategory/aa-1-13-8"
// }

The slug rule holds for every category in the taxonomy: the trailing aa-1-13-8 is the Shopify code, and its segment count is the depth. That gives you a clean join onto anything else built on the same taxonomy — a Shopify store, a Google Merchant feed — without us having to hand you a mapping table.

The shape of the tree

Depth is not a proxy for how specific a category is, and products are not all filed at the leaves. Expect to receive categories at any depth.

depth 0
26
Verticals
depth 1
218
depth 2
1,619
depth 3
4,704
depth 4
5,159
Most categories sit here
depth 5
2,252
depth 6
557
depth 7
71
Deepest

The 26 verticals

Every category descends from one of these. The counts show how much tree sits underneath each, which is worth knowing before you decide to render any of it — one vertical alone accounts for over a fifth of the taxonomy.

Sporting Goodssg3,079
Home & Gardenhg2,285
Arts & Entertainmentae1,256
Electronicsel1,175
Hardwareha1,121
Health & Beautyhb905
Food, Beverages & Tobaccofb763
Apparel & Accessoriesaa662
Vehicles & Partsvp646
Business & Industrialbi593
Furniturefr473
Animals & Pet Suppliesap417
Toys & Gamestg270
Baby & Toddlerbt251
Office Suppliesos242
Cameras & Opticsco211
Servicesse55
Softwareso48
Mediame37
Luggage & Bagslb36
Maturema36
Religious & Ceremonialrc12
Product Add-Onspa7
Bundlesbu
Gift Cardsgc
Uncategorizedna

Bundles, Gift Cards and Uncategorized have no children at all — a product filed under one of them is at a vertical, not in a leaf.

Don't render the whole tree

The full nested response is roughly 2 MB of JSON, and the overwhelming majority of it describes categories that no product on the platform is filed under. A menu built by walking the tree is mostly dead ends.

Build navigation from your own catalogue instead. Every product carries its category, so the set of categories worth showing is exactly the set your products reference — usually a few dozen, not 14,606.

Navigation from what you actually stock
// 14,606 categories exist. Only the ones your own products actually
// reference belong in a menu — build it from your catalogue, then use
// the tree for labels and ordering.
const inUse = new Map<
  string,
  { name: string; breadcrumb: string; products: number }
>();

for await (const product of paginate("/products")) {
  const category = product.category;
  if (!category) continue; // category is optional — always guard

  const entry = inUse.get(category.id) ?? {
    name: category.name,
    breadcrumb: index.get(category.id)?.breadcrumb ?? category.name,
    products: 0,
  };
  entry.products += 1;
  inUse.set(category.id, entry);
}

// Sort by breadcrumb and you have a menu that mirrors the taxonomy's
// own ordering, with nothing empty in it.
const menu = [...inUse.values()].sort((a, b) =>
  a.breadcrumb.localeCompare(b.breadcrumb)
);

How a product ends up in a category

Suppliers pick a category in the dashboard, or name one in a CSV import. For CSV, the text in the Category column is resolved in this order, and the first rule that matches wins.

1

Saved mapping

If the supplier has previously confirmed what a given piece of text means, that mapping wins. Case-insensitive.
2

Exact breadcrumb

The full path, matched exactly — Apparel & Accessories > Clothing > Clothing Tops > T-Shirts. This is the only unambiguous way to name a category in a CSV.
3

Exact name, if globally unique

A bare name is accepted only when exactly one category in the whole taxonomy carries it. T-Shirts is rejected at this step; Beeswax is accepted.
4

Unresolved

The row is flagged with the closest breadcrumb as a suggestion. The suggestion is never applied automatically — a wrong guess would silently mis-file the product, so a human confirms it.

Two consequences worth designing for. A product's category can be broader than you would file it yourself, because a supplier naming T-Shirts gets nothing and has to choose a specific branch. And category can be null — nothing forces a supplier to categorise a product, so guard for it rather than assuming every product has one.