Listings Tutorial
important
These tutorials are subject to change as endpoints change during our feedback period development. We welcome your feedback! If you find an error or have a suggestion, please post it in the Open API GitHub Repository.
Listings are the pages containing products for sale in an Etsy shop. The Etsy Open API v3 supports managing listings either for an individual shop or across the Etsy marketplace as a whole, depending on your application's Access Level.
Throughout this tutorial, the instructions reference REST resources, endpoints, parameters, and response fields, which we cover in detail in Request Standards and URL Syntax.
Authorization and x-api-key header parameters#
The endpoints in this tutorial require an OAuth token in the header with listings_r and listings_w scope. If your app also deletes listings, then the token requires the listings_d scope as well. See the Authentication topic for instructions on how to generate an OAuth token with these scopes.
In addition, all Open API V3 Requests require the x-api-key:  parameter in the header with your shop's Etsy App API Key keystring, which you can find in Your Apps.
Listing lifecycle and state#
After creating a listing, your users, Etsy, or your application can change the listing to reflect several states that determine how customers interact with the listing and the effective changes available to sellers, which map to API endpoints. The following table summarizes the states and the change operations available from the API.
| state | description | Actions and endpoints | 
|---|---|---|
| draft | inactive listing because its state is not "active" or lacks at least one image | Publish (updateListing), Delete (deleteListing) | 
| published | active listing searchable by users, with > 0 unsold inventory | Deactivate (updateListing), Delete (deleteListing) | 
| deactivated | previously published listing deliberately deactivated, unsearchable, and unsellable | Publish (updateListing), Delete (deleteListing) | 
| sold out | published listing with 0 unsold inventory | Delete (deleteListing) | 
| expired | previously published listing older that was not renewed after expiring (not charged) | Publish (updateListing), Delete (deleteListing) | 
Listing a physical product for sale#
To add a new listing to a shop, use the createDraftListing endpoint, which adds a single item for sale to an Etsy Shop. All published listings require at least one listing image, so your application must either:
- use images already uploaded to the shop, or
 - upload listing images - see Adding an image to a listing
 
The following procedure adds a listing using images already uploaded to the shop:
Form a valid URL for
createDraftListing, which must include ashop_idfor the shop that hosts the listing. For example, if your shop_id is : "12345678", thecreateDraftListingURL is:
Build the
createDraftListingrequest body, which must include at a minimum:quantitytitledescriptionpricewho_madewhen_madetaxonomy_idimage_idsrequired for active listingsshipping_profile_idrequired for physical listingsreadiness_state_idrequired for physical listings
Execute a
createDraftListingPOST request with yourlistings_wscoped OAuth token andx-api-key. For example, acreateDraftListingrequest to list 5 yo-yos might look like the following:
- JavaScript fetch
 - PHP curl
 
To sell variations of the same product in the same listing, such as different colored products with specific quantities for sale in each color, see Listing inventory with different properties, quantities, and prices below.
Listing a digital product for sale#
To list a digital product for sale, use createDraftListing just as you would for a physical product, but your application must set the listing's type parameter to "download" and upload a digital product file for the digital product listing using uploadListingFile. If you already uploaded a digital product file to your shop, for example as part of previous listing, you can associate the file with a listing using uploadListingFile with its file ID as well. Each file in a shop is unique and managed separately, so you cannot assign or upload a file with createDraftListing.
The following procedure uploads a digital product file to a listing and updates the listing's type parameter to "download":
Form a valid URL for
uploadListingFile, which must include ashop_idandlisting_idto assign the digital product file to a listing. For example, if yourshop_idis "12345678" and yourlisting_idis "192837465," then the uploadListingFile URL is:Build the
uploadListingFilerequest body, which must include either afile(binary) parameter for a digital product file to upload or afile_idfor a file already uploaded to the shop, but not both.Execute an
uploadListingFilePOST request with yourlistings_wscoped OAuth token andx-api-key. For example, anuploadListingFilerequest might look like the following:
- JavaScript fetch
 - PHP curl
 
- Set the listing's 
typeto "download" with anupdateListingPATCH request that includesshop_idandlisting_idin the URL, alistings_wscoped OAuth token andx-api-keyin the header, and thetypesetting in the request body. For example, anupdateListingrequest might look like the following: 
- JavaScript fetch
 - PHP curl
 
Converting a physical product listing to a digital product listing#
In the event that a physical product listing needs to be changed to a digital listing, this can be accomplished via the updateListing endpoint and passing type as "download". However, note that if the physical product listing has any variations or inventory beyond a single product, updateListing will return a 409 error. Before converting any physical listing to digital, the inventory must be reset to a single product using the uploadListingInventory endpoint.
The following is an example body of the uploadListingInventory post (set your price as a float value and your sku):
Adding an image to a listing#
Published listings require at least one listing image, as noted above. To upload a new image and add it to a listing, use the uploadListingImage endpoint with the shop and listing IDs, and add the image binary file in the image parameter. To make a listing active after uploading a required image, use the updateListing endpoint with the state parameter set to "active." As noted above, you can associate images with listings in a createDraftListing request using the image_ids parameter if images are already uploaded to your shop.
The following procedure uploads an image to a listing and updates the listing to active:
Form a valid URL for
uploadListingImage, which must include ashop_idandlisting_idto assign the image to a listing. For example, if yourshop_idis "12345678" and yourlisting_idis "192837465," then theuploadListingImageURL is:Build the
uploadListingImagerequest body, which must include either animage(binary) parameter with a digital image as its value or alisting_image_idfor an image uploaded to the shop, but not both.Execute an
uploadListingImagePOST request with yourlistings_wscoped OAuth token andx-api-key. For example, anuploadListingImagerequest might look like the following:
- JavaScript fetch
 - Node JS
 - PHP curl
 
- Set the Listing's 
stateto "active" with anupdateListingPATCH request that includesshop_idandlisting_idin the URL, alistings_wscoped OAuth token andx-api-keyin the header, and the newstatein the request body. For example, an updateListing request might look like the following: 
- JavaScript fetch
 - PHP curl
 
Listing inventory with different properties, quantities, and prices#
Inventory is a list of products for sale in a listing. The products are customizable, so understanding the inventory request structure is vital to offering different variations of the same product in one listing. Inventory defines products using the following components:
sku: Stock Keeping Unit (SKU) assigned to this product.offerings: a list of prices and quantities associated with a specific product, representing purchase options visible to buyers on the Etsy shop.quantity: the number of products available at this offering priceprice: a number indicating the price of this product interpreted in the default currency of the listing/shop, which is US pennies by default.is_enabled: when true, the offering is visible to buyers in the listing.readiness_state_id: the processing profile associated with the offering, if processing profile is set at listing level this id will be the same across all offerings.
property_values: A list of properties differentiating this product from other products in a listing. For example, to sell sets of bed sheets in different color (white, blue, magenta, forest green, etc) and size (twin, full, queen, king) combinations, use property_values for color and size.property_id: a unique number identifying this property.property_name: a string name for a property.scale_id: a number indexing an Etsy-defined scale. There are a lot of these, but for example shoe sizes have three available scales:
| Scale ID | Scale Name | Value IDs and Names | 
|---|---|---|
| 17 | US/Canada | value_id:1329,"name":"0 (Baby)", value_id:1330,"name":"0.5 (Baby)", value_id:1331,"name":"1 (Baby)", value_id:1332,"name":"1.5 (Baby)", value_id:1333,"name":"2 (Baby)", value_id:1334,"name":"2.5 (Baby)", value_id:1335,"name":"3 (Baby)", value_id:1336,"name":"3.5 (Baby)", value_id:1337,"name":"4 (Baby)", value_id:1338,"name":"4.5 (Walker)", value_id:1339,"name":"5 (Walker)", value_id:1340,"name":"5.5 (Walker)", value_id:1341,"name":"6 (Walker)", value_id:1342,"name":"6.5 (Walker)", value_id:1343,"name":"7 (Walker)", value_id:1344,"name":"7.5 (Toddler)", value_id:1345,"name":"8 (Toddler)", value_id:1346,"name":"8.5 (Toddler)", value_id:1347,"name":"9 (Toddler)", value_id:1348,"name":"9.5 (Toddler)", value_id:1349,"name":"10 (Toddler)", value_id:1350,"name":"10.5 (Toddler)", value_id:1351,"name":"11 (Toddler)", value_id:1352,"name":"11.5 (Toddler)", value_id:1353,"name":"12 (Toddler)", value_id:1354,"name":"12.5 (Youth)", value_id:1355,"name":"13 (Youth)", value_id:1356,"name":"13.5 (Youth)", value_id:1357,"name":"1 (Youth)", value_id:1358,"name":"1.5 (Youth)", value_id:1359,"name":"2 (Youth)", value_id:1360,"name":"2.5 (Youth)", value_id:1361,"name":"3 (Youth)", value_id:1362,"name":"3.5 (Youth)", value_id:1363,"name":"4 (Youth)", value_id:1364,"name":"4.5 (Youth)", value_id:1365,"name":"5 (Youth)", value_id:1366,"name":"5.5 (Youth)", value_id:1367,"name":"6 (Youth)", value_id:1368,"name":"6.5 (Youth)", value_id:1369,"name":"7 (Youth)" | 
| 18 | EU | "value_id":1370,"name":"15", "value_id":1371,"name":"16", "value_id":1372,"name":"17", "value_id":1373,"name":"18", "value_id":1374,"name":"19", "value_id":1375,"name":"20", "value_id":1376,"name":"21", "value_id":1377,"name":"22", "value_id":1378,"name":"23", "value_id":1379,"name":"24", "value_id":1380,"name":"25", "value_id":1381,"name":"26", "value_id":1382,"name":"27", "value_id":1383,"name":"28", "value_id":1385,"name":"29", "value_id":1386,"name":"30", "value_id":1387,"name":"31", "value_id":1388,"name":"32", "value_id":1389,"name":"33", "value_id":1390,"name":"34", "value_id":1391,"name":"35", "value_id":1392,"name":"36", "value_id":1393,"name":"37", "value_id":1394,"name":"38", "value_id":1395,"name":"39" | 
| 19 | UK | value_id:1396,"name":"0 (Baby)", value_id:1397,"name":"0.5 (Baby)", value_id:1399,"name":"1 (Baby)", value_id:1401,"name":"1.5 (Baby)", value_id:1402,"name":"2 (Baby)", value_id:1403,"name":"2.5 (Baby)", value_id:1404,"name":"3 (Baby)", value_id:1405,"name":"3.5 (Walker)", value_id:1406,"name":"4 (Walker)", value_id:1407,"name":"4.5 (Walker)", value_id:1408,"name":"5 (Walker)", value_id:1409,"name":"5.5 (Walker)", value_id:1410,"name":"6 (Walker)", value_id:1411,"name":"6.5 (Toddler)", value_id:1412,"name":"7 (Toddler)", value_id:1413,"name":"7.5 (Toddler)", value_id:1414,"name":"8 (Toddler)", value_id:1415,"name":"8.5 (Toddler)", value_id:1416,"name":"9 (Toddler)", value_id:1417,"name":"9.5 (Toddler)", value_id:1418,"name":"10 (Toddler)", value_id:1419,"name":"10.5 (Toddler)", value_id:1420,"name":"11 (Toddler)", value_id:1421,"name":"11.5 (Youth)", value_id:1422,"name":"12 (Youth)", value_id:1423,"name":"12.5 (Youth)", value_id:1424,"name":"13 (Youth)", value_id:1425,"name":"13.5 (Youth)", value_id:1426,"name":"1 (Youth)", value_id:1428,"name":"2 (Youth)", value_id:1429,"name":"2.5 (Youth)", value_id:1430,"name":"3 (Youth)", value_id:1431,"name":"3.5 (Youth)", value_id:1432,"name":"4 (Youth)", value_id:1433,"name":"4.5 (Youth)", value_id:1434,"name":"5 (Youth)", value_id:1435,"name":"5.5 (Youth)", value_id:1436,"name":"6 (Youth)" | 
value_ids: a list of numbers valid for thescale_idselected indicating the product variations.values: a list of strings matching the value ids selected.
The following endpoints change the listing properties and inventory for an existing listing:
updateListingPropertyadds properties to a listingupdateListingInventoryassigns skus to offerings for different property combinations
Updating Inventory#
The following procedure adds a product for sale in a listing:
Form a valid URL for
updateListingInventory, which must include alisting_idto change the inventory in a listing. For example, if yourlisting_idis "192837465," then the updateListingInventory URL is:Build the
updateListingInventoryrequest body, which must include at least one product in theproductsparameter with nestedofferingsandproperty_valueslists.Execute an
updateListingInventoryPUT request with alistings_wscoped OAuth token andx-api-key. For example, anupdateListingInventoryrequest to add 10 US/Canada size 4 shoes with a sku of 7836646 might look like the following:
- JavaScript fetch
 - PHP curl
 
NOTES:
When updating inventory, the entire set of products (based on variations) must be in the
productsarray.To get the product array, call
getListingInventoryfor the listing. From thegetListingInventoryresponse, remove the following fields:product_id,offering_id,scale_name,is_deletedandvalue_pairs. Also change thepricearray in offerings to be a decimal value instead of an array.The
*_on_propertyvalues should match theproperty_idvalues, but only if those properties affect the price, quantity or sku. See the sample below for handling variations.Use Custom Variations if any of the existing attributes don't exist or fit your use case.
Handling Variations in Inventory Updates#
The following example updates a listing inventory where there are two variations:
Material - "Pine", "Oak", "Walnut"
Size - "3", "4", "5"
In this example, the material variation affects the price, while the size variation affects the quantity and sku of the product.
In the products array, you will have 9 entries (3 materials x 3 sizes).
Due to the size affecting both quantity and sku, when quantity is updated it must be the same value across all products sharing the same sku. The sku "woodthing3" has 3 entries in the array, but the quantity value for all three of those arrays (inside offerings) must be the same value (33 in this example).
Similarly, because material affects pricing, in each product you will see that the "Walnut" property value indicates the price in offerings is 8.00 while "Oak" is 7.00 and "Pine" is 6.00.
Note that since there is only one variation that affects the price, the price_on_property value is a single value of 507, which is the property_id for "Material". Since size affects both quantity and sku, the quantity_on_property and sku_on_property values are a single value of 100, which is the property_id for "Size".
The processing profile is shared across all offerings, which means that the profile is set at a listing level. See Adding a processing profile to a listing for more details about processing profiles.
- JavaScript fetch
 - PHP curl
 
Custom Variations#
Custom Variations behave just like normal variations, except you can assign your own custom name to them instead of needing to adhere to attributes. Just like normal variations, there can be at most 2 variations. The property_ids for custom variations are 513 and 514. Use either of these when creating a custom variation.
How to Fetch Property Values for the Products Array?#
Since variations can not be added at the time of creation of a new listing, the following procedure should help with creating the products array for your updateListingInventory call.
The property_values is required but may be empty when attempting to post to the endpoint. The properties that may be used for variations on any given listing will depend upon the category the listing is placed in.
Property ids and possible values are available via the following endpoints: getSellerTaxonomyNodes and getPropertiesByTaxonomyId.
If you don't already have a list of property ids for the product properties you wish to use in the new listing, use a
GETcall togetSellerTaxonomyNodesfirst to retrieve the full hierarchy tree of seller taxonomy nodes.In the taxonomy tree, you can look for the category you wish to use and note the id.
Perform a
GETcall to thegetPropertiesByTaxonomyIdendpoint with the id of the category. This will give you the possible properties for the category, along with their possible values.
Notes about taxonomy:
Some of the common taxonomy node properties that Etsy uses, such as
Colorhave a list of common values/value ids. When you pass in value_id4we convert that to your shop's unique ID for that color. If it's not already in the system for your shop, it will create one. And then you'll use it over and over again any time you provide 'Green' for the color in the values field OR if you pass in 4 or your custom ID in the value_ids field and subsequent queries to get inventory should return that custom ID and not our known common ID.There are also
getBuyerTaxonomyNodesandgetPropertiesByBuyerTaxonomyIdendpoints for use by more buyer-facing apps. The difference between the two is that the levels of hierachy in the seller taxonomy is often deeper than that of Buyers. For example, a listing for "blue yarn" is inAll categories→Craft Supplies & Tools. TheCraft Supplies & Toolsis a buyer taxonomy. But in reality, the listing is inside ofAll categories→Craft Supplies & Tools→Yarn & Fiber→Yarn. TheYarn & FiberandYarncategory and sub category are the seller taxonomy. For sellers these are very useful categories for sorting and tracking listings. However, from a Buyer perspective, showing the category and subcategory in the category tree would end up cluttering the buyer experience. Since the yarn is really just a craft supply it makes sense to show it under that more top-level category.
Adding a shipping profile to a listing#
Shipping profiles assemble shipping details such as shipping price and processing time for recipients grouped by country or region. Every physical listing requires a shipping profile, so you add shipping profiles to your shop with createShopShippingProfile, and assign a shipping profile to a listing using a shipping_profile_id when you create or update the listing.
The following procedure creates a new shipping profile and returns the shipping_profile_id in the response:
Form a valid URL for
createShopShippingProfile, which must include ashop_id. For example, if yourshop_idis "12345678", then thecreateShopShippingProfileURL is:https://api.etsy.com/v3/application/shops/12345678/shipping-profiles
Build the
createShopShippingProfilerequest body, which must include at a minimum:
title: Use a title that indicates the country or region.origin_country_iso: The ISO code of the country from which the listing ships.primary_cost: The cost of shipping to this country/region alone, measured in the store's default currency.secondary_cost: The cost of shipping to this country/region with another item, measured in the store's default currency.min_processing_time: The minimum processing time required to process to ship listings with this shipping profile. This field is in deprecation phase and will be removed by Q1 2026.max_processing_time: The maximum processing time required to process to ship listings with this shipping profile. This field is in deprecation phase and will be removed by Q1 2026.- One of 
destination_country_iso(see list of Alpha-2 codes here) ORdestination_region(possible values are "eu" "non_eu" or "none"), but not both. 
- Execute an 
createShopShippingProfilePOST request with yourshops_wscoped OAuth token andx-api-key, and read the generatedshipping_profile_idfrom the response. For example, acreateShopShippingProfilerequest for shipments from the US to the EU with free shipping might look like the following: 
- JavaScript fetch
 - PHP curl
 
- Set the listing's 
shipping_profile_idto the shipping profile ID read from the response to setting the shipping profile ID with anupdateListingPATCH request that includesshop_idandlisting_idin the URL, alistings_wscoped OAuth token andx-api-keyin the header, and the new state in the request body. For example, anupdateListingrequest might look like the following: 
- JavaScript fetch
 - PHP curl
 
Adding a processing profile to a listing#
Processing profiles contain information about the readiness state and processing time of a product. Every physical listing requires at least one processing profile to be linked to it. You can create processing profiles for your shop with createShopReadinessStateDefinition, and link it to a listing using the readiness_state_id when you create a new listing through createDraftListing or to specific products when you update a listing's inventory through updateListingInventory.
The following procedure creates a new processing profile and returns the readiness_state_id in the response:
Form a valid URL for
createShopReadinessStateDefinition, which must include ashop_id. For example, if yourshop_idis "12345678", then thecreateShopReadinessStateDefinitionURL is:https://api.etsy.com/v3/application/shops/12345678/readiness-state-definitions
Build the
createShopReadinessStateDefinitionrequest body, which must include at a minimum:
readiness_state: The state a product can be in production wise, values can be either "ready_to_ship" or "made_to_order".min_processing_time: The minimum processing time required to process to ship listings with this shipping profile. This field is in deprecation phase and will be removed by Q1 2026.max_processing_time: The maximum processing time required to process to ship listings with this shipping profile. This field is in deprecation phase and will be removed by Q1 2026.processing_time_unit: The unit used to represent how long a processing time is. A week is equivalent to 5 business days. Values can be "days" or "weeks". If none is provided, the unit is set to "days".
- Execute a 
createShopReadinessStateDefinitionPOST request with yourshops_wscoped OAuth token andx-api-key, and read the generatedreadiness_state_idfrom the response, which you’ll use to link it to specific products later on. For example, acreateShopReadinessStateDefinitionrequest for a product that’s made to order and the processing time is 5-8 days might look like the following: 
- JavaScript fetch
 - PHP curl
 
- Set the listing's 
readiness_state_idto the readiness state ID read from the response while creating a new listing throughcreateDraftListingendpoint or while updating the listing throughupdateListingInventoryendpoint. For example, anupdateListingInventoryrequest might look like the following: 
- JavaScript fetch
 - PHP curl
 
As you can see in the above example, since processing profiles vary by the material property like price, we'll add a readiness_state_on_property value with the property_id corresponding to "Material" (507).
Each product offering will have an associated readiness_state_id, in this case the same  readiness_state_id needs to be used for all the offerings that have the same material. We have two offerings for each material, let’s take “Walnut” as an example: 
- Walnut - Size 4
 - Walnut - Size 5
 
Since they all share the “Walnut” property value they will use the same processing profile.
However, if you’d prefer to set the processing profile at a listing level, the body request for the updateListingInventory endpoint would be pretty similar to the case above, with two main differences:
- The same 
readiness_state_idwill be used for all the product offerings. - The 
readiness_state_on_propertyvalue will be empty, as none of the properties change the readiness state of the product.