Open API v2 will be deprecated on April 3, 2023. Please begin transitioning to Open API v3 as soon as possible. As of September 29, 2022 all new apps will only be permitted to use Open API v3.

API Documentation

Working with Inventory

Overview

We've launched new tools for all sellers to allow them to represent their inventory more accurately in their listings, giving them more control over how they set their prices and manage their stock.

We’re also rolling out additional tools around “structured data” - ways to allow sellers to describe their items in ways that have a consistent meaning to buyers (and - importantly - to our search systems).

These updates are exciting, but they do have implications for the Open API and possibly for you as a developer!

This document will cover inventory management terminology and concepts, the changes to the API (new endpoints, changed endpoints, and deprecated endpoints), example use cases and how to tackle them, and a few implementation and rollout notes.

Before we get into that, a (very brief) summary of what changed:

  1. Price and quantity are no longer editable on the listing.
  2. Price and quantity on the listing are guideline figures.
  3. Variations are no longer writable (and the existing variations endpoints will be deprecated).
  4. A new “inventory” data structure represents available stock options, and their quantities and prices.
  5. New endpoints allow developers to access and edit structured data attributes on listings.

What is inventory?

Put simply, inventory is the stuff a seller has to sell. This may be actual ready-to-go stock, or it may be items the seller is able to make to order given the materials on hand (and their capacity for making them).

Products

Historically, these have been represented on Etsy as a Listing. We introduced variations to allow sellers to express different options for a given listing, but that has had some limitations.

With the work on inventory management, we’ve introduced a separation between a listing and the actual things it represents.

Imagine I’m a supply seller, and I have a listing for a glass bead. I sell them in red and blue. I also have the same bead in 6mm and 10mm diameters. The listing actually represents four different physical things - red 6mm bead, blue 6mm bead, red 10mm bead, and blue 10mm bead. We refer to these individual things as products. A product is the actual thing a seller ships.

You’ll notice the parallels here between products and variations - we’re maintaining variations in the user interface for sellers and just storing and handling them in a new way on the back end. For the time being, we still have the restriction of a maximum of two variations, so products can have at most two different properties. With no variations, you have a single product. With one variation, you have a product for each option in that variation. With two variations, you have products for each combination of options.

Each product can be given an optional product identifier (we’re labelling it “SKU”) so that sellers can link products on Etsy back to their physical inventory tracking process if they wish.

Offerings

An offering is an instance of a product with a specific price and quantity. Right now, there's a 1:1 mapping of products to offerings, but splitting the two allows us some room for future expansion, hopefully without making disruptive changes to the API.

By having price and quantity on offerings, we give sellers a lot more flexibility in how they specify what they have for sale. For example, maybe I got a good deal on the 6mm beads and I’d like to pass that on to my buyers, but the 10mm ones were more pricey. I have 1,000 each of both sizes of the red beads, but for some reason the manufacturer is having serious trouble with the blue ones. I’m down to 100 in the 6mm, and I’m out of the 10mm. That’s impossible to express with variations as they currently are! Sellers are obliged to create multiple listings or use other workarounds to handle it, and may run the risk of selling things they don’t have (or not selling as much of some things as they could). With this new approach, each product can be managed separately, simplifying the process of listing items considerably.

Attributes

We allow listings to have metadata as attributes - in other words, things that apply to all products represented by the listing. In our bead example, we might specify that the material is “glass”.

There will be endpoints in the API to allow you to determine what attributes are available for a given listing, and what sorts of values can be set for them.

Structured data

The available attributes and product properties for a given listing - and their semantics - depend on the category that the seller has chosen for them. A glass bead might have a diameter, whereas a shoe would not. A shoe would have a size, but the scale and meaning of the size (e.g. “US women’s 10”) would be different from the size of a t-shirt (e.g. “Men’s XL”).

The values that sellers can assign to their products’ properties and listings' attributes may now be structured - in other words they can express specific meaning. In our bead example, “6mm” and “10mm” have meaning - they represent the physical diameter of the bead. Given the value and the scale, we can say things about the item - for example, if I’m looking for a 1cm bead that 10mm bead would be perfect, whereas if I need something smaller than 0.25” then the 6mm bead would be the one. Similarly if we know that “US women’s 10” refers to a specific shoe size, we can help buyers find your item no matter what scale they are using to find a shoe on Etsy by translating “US women’s 10” into the buyer’s preferred scale.

Structured data will allow us to provide buyers with more meaningful search and browsing experiences, and we’re really excited to be opening it up to our sellers.

Migrating to the new API endpoints

We understand that developers can't change their apps overnight! As such, we have a few mechanisms that will hopefully help the transition.

Backwards compatibility

If a listing is backwards-compatible, it is one that could be managed using the old tools we provided sellers before the launch of the Inventory Management project. Existing (deprecated) endpoints will continue to work on backwards-compatible listings for the time being, but will be disabled in the near future.

Listings cease to be backwards compatible if they use new features like structured data attributes, having quantity depend on a variation, etc. New listings are now automatically upgraded to be compatible with the Inventory Management API within several days of being created. Therefore we encourage developers to transition their apps to use the Inventory Management API.

Inventory Management app compatibility

Apps that don't write listings should continue to work, so we mostly concern ourselves with apps that have listings_w permission. Those apps are flagged in the Your Apps page as being possibly incompatible, with a warning that we can't guarantee they'll work, since it depends on exactly what calls and changes the app is making and on the backwards-compatible status of a given listing. A more general warning is shown on the Listing Manager page and the listing edit screen.

As a developer, once your app is updated to use the new endpoints (or if your app doesn't need to be updated because it doesn't use them) you can mark your app as compatible by editing it from the Apps You've Made page and checking the "Compatible with the new Inventory Management API" box and sellers who've authorized your app will no longer see it flagged as potentially problematic.

If your app has listings_w permission, you must confirm that it works with the new API and mark it as compatible in Your Apps.

The Inventory Management API

The following forms a baseline of the changes we have rolled out to support the launch of Inventory Management to all our sellers. There may be further enhancements to the API as we add new features but they should be backwards-compatible with what is described here. As such, these are what we believe the minimum endpoints needed to build useful tools around inventory (see use cases later in this document). If there are things you find particularly difficult or impossible to achieve, please let us know.

Listings

Reading listings

In returned Listing resources, price and quantity will represent the minimum possible price given all available offerings, and quantity will be the sum of all available offerings. Specific prices and quantities will have to be retrieved from Inventory endpoints.

There is an Inventory association, but note that it can only be called on individual listings, not on endpoints that return sets of listings.

Writing listings

For convenience, when creating listings, the passed price and quantity will be used to create a single product and offering record for the listing. If more complex inventory is needed, create the listing with the state ‘draft’ and then use Inventory endpoints to replace these with more products and offerings before activating the listing.

When editing listings, changes to price and quantity will be ignored. Use the updateInventory endpoint to update products and offerings.

When creating a listing, it is possible to supply a taxonomy_id. If a listing doesn't have a taxonomy_id, it can't have any structured data attributes. All listings will in time need a taxonomy_id - you are strongly encouraged to move your code over to use taxonomy_id and the Taxonomy endpoints as soon as possible. Note that listings return a suggested_taxonomy_id field to help with migration if needed.

Variations

Reading variations

You will still be able to read variations through the API (at least for the time being), although prices will be incorrect when prices depend on multiple properties (the price_diff will be set to zero). As such, the Variations association is currently considered deprecated, but hasn't been removed. This association can still be helpful in retrieving values for variation property data.

The existing methods to read variations may return inaccurate pricing information. We recommend getting variation data from the inventory endpoint.

Writing variations

The following write endpoints have been fully deprecated:

Variations will be inferred from products created using ListingInventory endpoints.

Variation Limits

As in the legacy variation endpoints, we allow up to 70 options for each variation property and up to two properties. These combinations generate up to 4900 unique products.

Inventory

Reading inventory

The getInventory method and Inventory association are the ways to access inventory data. They return a complex JSON data structure describing a listing's products and offerings.

Not all listings on Etsy have inventory records - they are generated on the fly where needed until a seller edits a listing, at which point they're written out to the database. As such, some calls to getInventory may return products and offerings with null IDs - this just means that you're seeing autogenerated inventory records. If you need product or offering IDs for your app, and you want to be certain that you'll get them and they'll be stable, then append write_missing_inventory=1 to the request. This will cause the API to write out the inventory to the database and return real IDs. (If you're using the Inventory association, adding this parameter will still work.) We want to be very intentional about when and where we write to the database, especially during a GET request, hence requiring this explicit request from you rather than just writing out records on read as standard.

Product and offering IDs are stable until new products or offerings are added, at which point IDs will likely change for all products and offerings, since we re-generate inventory based on all the permutations of variation properties a seller provides, rather than trying to selectively add or remove records.

The returned data consists of a JSON blob of products (and their offerings), plus fields telling you which properties control price, quantity, and SKU (if any) - so price might depend only on the color property, or SKU might depend on both color and size for example.

There are getProduct and getOffering endpoints. These will allow you to see old product and offering records if needed. Mostly we'd expect these to be useful if you're looking at historical offering data from the product_data value in a returned Transaction record.

With the new endpoints, we've also introduced a new Money resource that gives you more detail information about monetary values in localizable currencies.

Remember, a product is a specific item for sale - a large, red t-shirt, for example. Each product will specify which properties it has and their values (size large, color red in this example) in the form of PropertyValue resources.

Writing inventory

There is (currently) only an updateInventory method - i.e. you must update all the inventory even if you're making changes to a single offering. Because products and offerings can be linked (i.e. share a price, quantity, and/or SKU depending on the value of a variation or variations) making changes to a specific product or offering could have side effects on other records, and for now we want to make sure we're very clear about what you're changing.

When sending inventory, you may need to tell us which variation property or properties control price, quantity, or SKU - this is where the *_on_property fields come in. (Note that they are an "array" type of input, which in the Etsy API refers to a comma-separated list and not a JSON array.) The prices, quantities, and SKUs you send must be consistent with the controlling variations you send.

When specifying properties for your products, use the getTaxonomyNodeProperties method to find out which properties are available for use as variations for your listing (the supports_variations field is true for properties that are valid for use as variation properties). Note that this uses a taxonomy_id not a category_id - listings need to be updated to use the new taxonomy.

You can take the returned value from getInventory, modify it, and send it back again with updateInventory.

Attributes

Reading attributes

Listings can now have structured data properties to help sellers describe them in ways that we can search more accurately. The getAttributes and getAttribute endpoints allow you to read these. They'll be returned as PropertyValue resources.

Writing attributes

The updateAttribute and deleteAttribute methods can be used to add, modify, and remove listing attributes. (Adding an attribute can be done with updateAttribute too.)

There is not currently a method for updating attributes in bulk.

As with inventory properties, the key here is knowing which attributes can be used. Again, the getTaxonomyNodeProperties method is used here, but check the supports_attributes field to see if a given property is appropriate for use as an attribute.

Taxonomy

The new seller taxonomy is an important part of the new Inventory Management system. The properties available for a given listing depend on its taxonomy ID, as do the meanings of those properties ("size" means different things for shoes, clothes, and artwork, for example). As such, given a taxonomy ID, you should use getTaxonomyNodeProperties to find all the available properties for that node.

Some properties have constrained sets of allowed values ("Occasion", for example) where only certain value IDs will be accepted. Others allow a mix (such as "Primary Color", where you can use some pre-picked colors, or supply your own).

Some augment the supplied values with scales to provide context for the meaning of the value (such as "Length", where you specify both the numerical value of the field, and the ID of the scale that should be applied (centimeters, inches, etc.).

The taxonomy is versioned, and may change as we increase its scope over time. Use the getSellerTaxonomyVersion method to get a hash identifying the current version. If you cache responses to getTaxonomyNodeProperties you may want to periodically check the version in order to know when to re-fetch the data.

Working with Inventory Management API endpoints

Although we can't foresee every use case, below are a few use cases we can imagine, and will hopefully give you a flavor of how the new endpoints work.

In order to keep things simple and focus on the API interactions, for interactions requiring OAuth these examples assume a suitable OAuth object has been created and that the user has properly authorized the app with the necessary scopes. They also don't do any error checking! Please see the documentation on Making Requests for some examples of errors you might check for.

These examples are in PHP and use PHP's OAuth library, but are just intended to give a sense of how code might look. Any language capable of making HTTP requests can be used to access the API.

Creating a simple listing

If we consider the simple case of a listing with no variations or attributes, there's nothing different in what you need to do. We'll do the creation of the relevant inventory records in the background.

$data = $oauth->fetch(
    $etsy_base . '/listings',
    [
        'quantity'      => 1,
        'taxonomy_id'   => 1431,
        'title'         => 'Baby shoes',
        'description'   => 'Cute little shoes!',
        'price'         => 42.00,
        'currency_code' => 'USD',
        'who_made'      => 'i_did',
        'is_supply'     => 0,
        'when_made'     => 'made_to_order'
    ],
    OAUTH_HTTP_METHOD_POST
);


$inventory_uri = sprintf(
    '%s/listings/%d/inventory',
    $etsy_base,
    $data['listing_id']
);
$inventory = $oauth->fetch(
    $inventory_uri,
    null,
    OAUTH_HTTP_METHOD_GET
);


/*
{
    "price_on_property" => [],
    "quantity_on_property" => [],
    "sku_on_property" => [],
    "products" => [
        {
            "product_id" => 1234,
            "properties" => [],
            "offerings" => [
                {
                    "offering_id" => 1235,
                    "price" => {
                        "amount" => 4200,
                        "divisor" => 100,
                        "currency_code" => "USD",
                        "formatted_raw" => "42.00",
                        "formatted_short" => "US$42.00",
                        "formatted_long" => "$42.00 USD",
                        "original_currency_code" => "USD"
                    },
                    "quantity" => 1,
                    "is_enabled" => 1
                }
            ]
        }
    ]
}
*/

Creating a complex listing with two variations

First, we need to know what properties we can vary on for a listing with a given taxonomy_id:

$properties_uri = sprintf(
    '%s/taxonomy/seller/%d/properties',
    $etsy_base,
    $taxonomy_id
);
$properties = $oauth->fetch(
    $properties_uri,
    null,
    OAUTH_HTTP_METHOD_GET
);

The response is too long to include here, but you can see a sample.

In the sample response, you'll see this taxonomy node has seven possible properties - Primary Color, Holiday, Occasion, Secondary Color, Kids' Shoe Size, Custom Property 1, and Custom Property 2. Of these, Color, Secondary Color, Kids' Shoe Size, Custom Property 1, and Custom Property 2 are marked as "supports variations".

Let's make a listing with Shoe Size (property_id 18107358732) and a custom property "Fastener Type" (property_id 513). It's worth noting that properties have an order - here we'll make shoe size the first property and fastening the second.

For shoe size, we can pick a scale. There are three available - UK, US/Canada, and EU. US/Canada has scale_id 19. We can find corresponding values in the possible_values array which have a scale_id of 19 - "0 (Baby)" through to "13.5 (Youth)". We'll make tiny shoes - size 0 (value_id 1396) and 0.5 (value_id 1397) only.

For the custom property, we have to specify our own values. We'll call the property "Fastener type" and give it the values "Hook and loop" and "Ribbon laces".

In the Inventory Management part of the listing process on the website, we ask whether price, quantity, or SKU each vary based on a given property. In other words, you can have one price regardless of what variations are chosen, or have the price depend on size, or have it depend on size and fastener. (And so on for quantity and SKU.) We'll make things suitably complex here and say that price depends on fastener while quantity depends on size.

So this gives us the following:

Size Fastener Price Quantity
0 Hook and loop $42.00 10
0 Ribbon laces $40.00 10
0.5 Hook and loop $42.00 5
0.5 Ribbon laces $40.00 5

We take our listing and add inventory:

$property_size = 18107358732;
$property_fastener = 513;

$sizes = [
    [
        'property_id' => $property_size,
        'value_ids'   => [1396],
        'scale_id'    => 19,
    ],
    [
        'property_id' => $property_size,
        'value_ids'   => [1397],
        'scale_id'    => 19,
    ],
];

$fasteners = [
    [
        'property_id'   => $property_fastener,
        'property_name' => 'Fastener Type',
        'values'        => ['Hook and loop'],
    ],
    [
        'property_id'   => $property_fastener,
        'property_name' => 'Fastener Type',
        'values'        => ['Ribbon laces'],
    ],
];

$products = [
    [
        'property_values' => [$sizes[0], $fasteners[0]],
        'sku'             => '',
        'offerings'       => [
                                 [
                                     'price'      => 42.00,
                                     'quantity'   => 10,
                                     'is_enabled' => 1
                                 ]
                             ]
    ],
    [
        'property_values' => [$sizes[0], $fasteners[1]],
        'sku'             => '',
        'offerings'       => [
                                 [
                                     'price'      => 40.00,
                                     'quantity'   => 10,
                                     'is_enabled' => 1
                                 ]
                             ]
    ],
    [
        'property_values' => [$sizes[1], $fasteners[0]],
        'sku'             => '',
        'offerings'       => [
                                 [
                                     'price'      => 42.00,
                                     'quantity'   => 5,
                                     'is_enabled' => 1
                                 ]
                             ]
    ],
    [
        'property_values' => [$sizes[1], $fasteners[1]],
        'sku'             => '',
        'offerings'       => [
                                 [
                                     'price'      => 40.00,
                                     'quantity'   => 5,
                                     'is_enabled' => 1
                                 ]
                             ]
    ],
];

$inventory_uri = sprintf(
    '%s/listings/%d/inventory',
    $etsy_base,
    $listing_id
);
$data = $oauth->fetch(
    $inventory_uri,
    [
        'products'             => json_encode($products),
        'price_on_property'    => $property_fastener,
        'quantity_on_property' => $property_size,
        'sku_on_property'      => ''
    ],
    OAUTH_HTTP_METHOD_PUT
);

There is an element of repetition in some of this! This is conscious and deliberate in order to reduce the complexity of the API (fewer rules like "if you set the price here then we'll infer the price over here" etc.). Our assumption is that code is good at managing repetition, and you're best placed to make decisions about complexity in your own code.

Note that this endpoint will validate that the data sent is consistent with the linkages you've specified. So for example, if you say that price doesn't vary on any properties and then send offerings with different prices, the API will throw an error.

Updating A Listing With One Product

Every listing has at least one Product. A listing will have only one product if there are no variations, or if each variation has exactly one value (which is unusual but allowed).

For example, if you create a listing with no variations using the createListing method and fetch its inventory you’ll get back something like

{
    "count": 4,
    "results": {
        "products": [
            {
                "product_id": 245457,
                "sku": "",
                "property_values": [],
                "offerings": [
                    {
                        "offering_id": 324082,
                        "price": {
                            "amount": 1000,
                            "divisor": 100,
                            "currency_code": "USD",
                            "currency_formatted_short": "$10.00",
                            "currency_formatted_long": "$10.00 USD",
                            "currency_formatted_raw": "10.00"
                        },
                        "quantity": 1,
                        "is_enabled": 1,
                        "is_deleted": 0
                    }
                ],
                "is_deleted": 0
            }
        ],
        "price_on_property": [],
        "quantity_on_property": [],
        "sku_on_property": []
    },
    "params": {
        "listing_id": "212024164",
        "write_missing_inventory": false
    },
    "type": "ListingInventory",
    "pagination": {}
}
Note that there is a single product. If you need to update the listings title, description, etc, you'll continue to use the updateListing method. However, we encourage you to use the inventory endpoint to make updates to price and quantity as follows:

// Assumes products is the "products" portion of the above
$products[0]['offerings'][0]['quantity'] = 100;
// Note we remove the entire price section and replace it with a string float.
$products[0]['offerings'][0]['price'] = '50.00';

$inventory_uri = sprintf(
    '%s/listings/%d/inventory',
    $etsy_base,
    $listing_id
);
$data = $oauth->fetch(
    $inventory_uri,
    [
        'products' => json_encode($products),
    ],
    OAUTH_HTTP_METHOD_PUT
);
Fetching the listing again, the price and quantity will reflect this update.

Updating a listing's variations

If you need to update a listing's variations - adding or removing a property, adding or removing values, or changing then you'll need to basically follow the same process as creating the inventory in the previous example. Again, this reduces the complexity of the API and therefore the chance of surprising you (and your users) with unexpected side effects.

You may find it efficient to get the inventory blob, edit it, and then re-submit it.

Adding, updating, or removing listing attributes

As with our product properties, we need to look up the available attributes by the taxonomy_id of our listing, but this time filter them by "supports_attributes". In the case of our shoes example, we have Primary Color, Holiday, Occasion, Secondary Color, and Kids' Shoe Size. (We've already used shoe size as a variable property, so we can't use that one as a listing-level attribute.) Let's say these ones are white with some red on them. Primary Color has a property_id of 200 and White has a value_id of 10. Secondary Color has a property_id of 18107419236 and Red has a value_id of 9.

$primary_color_uri = sprintf(
    '%s/listings/%d/attributes/200',
    $etsy_base,
    $listing_id
);
$inventory = $oauth->fetch(
    $primary_color_uri,
    [
        'value_ids' => [10],
    ],
    OAUTH_HTTP_METHOD_PUT
);

$secondary_color_uri = sprintf(
    '%s/listings/%d/attributes/18107419236',
    $etsy_base,
    $listing_id
);
$inventory = $oauth->fetch(
    $secondary_color_uri,
    [
        'value_ids' => [9],
    ],
    OAUTH_HTTP_METHOD_PUT
);

Updating an attribute is exactly the same as creating it - a PUT on the relevant attribute URI.

Deleting is simply a DELETE on the relevant attribute URI.

Asking questions

The Seller Tools Developer Prototype was set up while we iterated on the new API endpoints. It has been a useful place to have discussions, give feedback, report bugs, etc., and is a little bit more focussed than the public API mailing list. You're encouraged to ask questions there. You can also contact developer@etsy.com if you'd like to ask questions in private, send example payloads or responses, etc. to help us identify any issues.

Open API v3New

Your developer account