How to work with the Woocommerce REST API with Python

WordPress is probably the most used CMS in the world (it is estimated that almost 40% of all websites are built using the platform): it is very easy to install and use, and allows even non-developers to create website in few minutes.
Wordpress has a very large plugin ecosystem; one of the most famous is Woocommerce, which allows us to turn a website into an online store in few steps. The plugin makes use of the WordPress REST API infrastructure; in this tutorial we will see how to interact with the Woocommerce API using the Python programming language, showing how to list, create, update and delete products and categories.

In this tutorial you will learn:

  • How to generate Woocommerce REST API credentials and enable pretty permalinks
  • How to interact with the Woocommerce REST API using Python and the woocommerce package
  • How to get information about the existing Woocommerce categories, create, update and delete them
  • How to get information about the existing Woocommerce products
  • How to create simple and a variable products with variations
  • How to update and delete a product

woocommerce-rest-api

Software requirements and conventions used

Software Requirements and Linux Command Line Conventions
Category Requirements, Conventions or Software Version Used
System Distribution independent
Software Python3
Other A working instance of WordPress with the Woocommerce plugin installed
Conventions # – requires given linux-commands to be executed with root privileges either directly as a root user or by use of sudo command
$ – requires given linux-commands to be executed as a regular non-privileged user

Generating Woocommerce REST API credentials

For the sake of this tutorial, we will assume we have a working instance of WordPress with the Woocommerce plugin already installed. The first thing we have to do, is to generate our Woocommerce REST API credentials: they will be
used in each HTTP request we will perform. Generating the credentials it’s very easy; all we have to do is to navigate to woocommerce -> settings in the vertical menu we can find in the WordPress administration page:

woocommerce-menu

Once in the plugin settings page, we click on the “Advanced” tab, and then on
the “REST API” link, which is located under the tab menu. In the page that will
be opened, we click on the “Create an API key” button:

woocommerce-create-key-page

We will be presented with the API key creation form, and we will be prompted to insert:

  • A description, which will be used as a friendly name to easily identify the credentials
  • The user which will make use of the key
  • The permissions that will be granted to the key (read only|write only|read and write)

Notice that we have the chance to create several keys with different permissions, in order to limit the operations granted for a specific user. For the sake of this tutorial we will create an API key with read and write permissions:

woocommerce-create-key-form

When ready, we click on the “Generate API key” button, and both the consumer key and the consumer secret will be generated and displayed to us. We have to make sure we store both in a safe place, since once we leave the page, they will be hidden:

woocommerce-key-secret

Once our keys are generated, there is another action we need to perform from the WordPress administration backend: we have to make sure that the right pretty permalink are used, otherwise the API endpoints will not work. To accomplish the task we navigate to Settings -> permalinks in the left vertical menu of WordPress. In the page menu, we select “Post name”, and then we save the changes:

wordpress-post-permalinks


That’s all we have to do WordPress-side. In the next section, we will see how to interact with the Woocommerce REST APIs using Python.

Install the woocommerce package

In this section we will see how to interact with the Woocommerce REST API using the Python programming language. Instead of writing the code we need to perform the HTTP requests from scratch, we will make use of the woocommerce package, which will ease our work. To install the package we can use pip, the Python package manager. If we are working inside a virtual environment created using venv, we can run:

$ pip install woocommerce

If we are not using a virtual environment, at least we should install the package for our user only. To do that, we add the --user option to the command, which becomes:

$ pip install woocommerce --user

Once the woocommerce package is installed, we can start writing our code.

Initializing the API class

The first thing we have to do to interact with the Woocommerce REST API, using Python and the woocommerce package, is to import the API class and create an instance of it, as displayed below:

#!/usr/bin/env python3
from woocommerce import API

wcapi = API(
  url="http://localhost",
  consumer_key="ck_147eb955838043597462c3f9c4f91cba08498159",
  consumer_secret="cs_55a72d6c7bde09b7f36befed32aa90709261c097",
  timeout=50
)

The API class constructor takes three mandatory arguments:

  1. The URL of our site
  2. The Woocommerce REST API consumer key
  3. The Woocommerce REST API consumer secret

In the example above, you can see we passed a third argument, timeout: it is optional, and defaults to 5 seconds. In this case we provide a larger value for it: 50. In my case this was necessary for the requests to succeed, but in a real
life scenario, we shouldn’t need to change it, so it can be omitted altogether.

Once we create an instance of the API class, in this case referenced by the wcapi variable, we can proceed and making our API calls.

Categories

For the sake of this tutorial we will only work only with categories and products. The provided examples should be enough to give the reader an idea of how the API works. Let’s start with categories, since they should pre-exist in order to be referenced when creating products.

Creating a category

As the first example, we will see how to create a category. We start defining the category data in a Python dictionary:

category_data = {
    "name": "Example category",
    "description": "Just a category example"
}

The only mandatory parameter we should use when creating a category, is name, which should be provided as a string. In the example above we specified also the description key, to provide a brief description of the category (you can check the official documentation for the complete list of category properties).

Once we created the dictionary containing the category data, we can execute the API request used for creating the category, which uses the POST HTTP verb:

response = wcapi.post("products/categories", category_data)

If the request is executed without errors, calling the json method on the response object, will return the server response formatted as a Python dictionary, which describes the data used to create the category: this will include the unique ID used to store the category in the database, which is an essential information if we want to reference it later (for example when creating a subcategory or a product which should be included under the category itself). In our case, here is what is returned by the server:

{
  'id': 17,
  'name': 'Example category',
  'slug': 'example-category',
  'parent': 0,
  'description': 'Just a category example',
  'display': 'default',
  'image': None,
  'menu_order': 0,
  'count': 0,
  '_links': {
    'self': [
      {'href': 'http://localhost/wp-json/wc/v3/products/categories/17'}
    ],
    'collection': [
      {'href': 'http://localhost/wp-json/wc/v3/products/categories'}
    ]
  }
}

As we can see, the category has been saved with 17 as unique id.

Updating a category

To update an existing category we must reference it via its id, which should be included as part of the request endpoint. First we create a dictionary which contains the category data which should be updated; in the example below we change the category description:

category_data = {
  "description": "Modified category example"
}

Once the data is ready we can send our request, using the put method of the wcapi object, which, as you can guess, sends a request using the PUT HTTP verb:

response = wcapi.put('products/categories/17', category_data)

Just as before, by executing the json method of the response object, we will retrieve the updated category information returned by the server, already converted to a Python dictionary:

{
  'id': 17,
  'name': 'Example category',
  'slug': 'example-category',
  'parent': 0,
  'description': 'Modified category example',
  'display': 'default',
  'image': None,
  'menu_order': 0,
  'count': 0,
  '_links': {
    'self': [
      {'href': 'http://localhost/wp-json/wc/v3/products/categories/17'}
    ],
    'collection': [
      {'href': 'http://localhost/wp-json/wc/v3/products/categories'}
    ]
  }
}

Obtaining information about all categories or a specific one

Obtaining a list of all the existent categories is really simple. All we have to do is to execute the get method of the wcapi object we created before, and specify the correct endpoint (products/categories):

response = wcapi.get('products/categories')

As before the content of the response can be accessed as a python dictionary by executing the json method on the response object. In this case the method returns the following:

[
  {
    'id': 17,
    'name': 'Example category',
    'slug': 'example-category',
    'parent': 0,
    'description': 'Just a category example',
    'display': 'default',
    'image': None,
    'menu_order': 0,
    'count': 0,
    '_links': {
      'self': [
        {'href': 'http://localhost/wp-json/wc/v3/products/categories/17'}
      ],
      'collection': [
        {'href': 'http://localhost/wp-json/wc/v3/products/categories'}
      ]
    }
  },
  {
    'id': 16,
    'name': 'test',
    'slug': 'test',
    'parent': 0,
    'description': 'a test',
    'display': 'default',
    'image': None,
    'menu_order': 0,
    'count': 0,
     '_links': {
       'self': [
         {'href': 'http://localhost/wp-json/wc/v3/products/categories/16'}
        ],
        'collection': [
          {'href': 'http://localhost/wp-json/wc/v3/products/categories'}
        ]
      }
    },
    {
      'id': 15,
      'name': 'Uncategorized',
      'slug': 'uncategorized',
      'parent': 0,
      'description': '',
      'display': 'default',
      'image': None,
      'menu_order': 0,
      'count': 0,
      '_links': {
        'self': [
          {'href': 'http://localhost/wp-json/wc/v3/products/categories/15'}
        ],
        'collection': [
          {'href': 'http://localhost/wp-json/wc/v3/products/categories'}
        ]
      }
    }
  ]

If we want to retrieve information about a specific category, all we have to do is to provide its id as part of the endpoint. For example, to obtain details about the category with id 16 (test), we would run:

response = wcapi.get('products/categories/16')

Deleting a category

The id of a category is needed also to reference it when we want to delete it. In those cases we should run an HTTP request which uses the DELETE HTTP verb, again providing the category identifier as part of the endpoint. To delete the “test” category, for example, we would run:

response = wcapi.delete('products/categories/16', param={'force', True})

When executing the delete method to delete a category, we also need to use the force parameter, and set it to True. This is required, since moving a category to the trash it is not supported via REST API; the resource will be permanently removed. If everything goes as expected, just as in the previous examples, calling the json method on the response object, will return a dictionary containing the removed resource data.

Performing multiple actions at once

Suppose we want to perform multiple actions at once: we may want to delete some categories, create some new ones, and update others. How can we do it at once, by executing just one request? All we have to do is to send a request using the POST HTTP verb to the products/categories/batch endpoint, using the post method of the wcapi object. Here is an example:

batch_data = {
  "create": [
    {
      "name": "New category 1",
      "description": "First new category"
    },
    {
      "name": "New category 2",
      "description": "Second new category"
    }
  ],
  "update": [
    {
      "id": 17,
      "description": "updated description"
    }
  ],
  "delete": [
    15
  ]
}


The batch data is defined, just like we saw in the previous examples, using a Python dictionary. Inside this dictionary we have some keys named after the actions that should be performed:

  • create
  • update
  • delete

The value assigned to the create key must be a list of dictionaries, each describing the data which should be used to create a new category. In this case we created two new categories, named “New category 1” and “New category 2”.

Similarly, the value corresponding to the update key must be a list of dictionaries, each describing the data which should be used to update a certain category, identified by its id.

Finally, the value associated to the delete key must be a list of the id of the categories which should be delete. In this case we decided to remove the category with 16 as unique identifier. Once our data is ready, we execute the
request:

response = wcapi.post('products/categories/batch', batch_data)

The response object will contain the object oriented representation of the response sent by the server. As always, by executing the json method of this object we will retrieve the Python dictionary containing a summary of the performed operation, and the description of the categories involved:

{
  'create': [
    {
      'id': 18,
      'name': 'New category 1',
      'slug': 'new-category-1',
      'parent': 0,
      'description': 'First new category',
      'display': 'default',
      'image': None,
      'menu_order': 0,
      'count': 0,
      '_links': {
        'self': [
          {'href': 'http://localhost/wp-json/wc/v3/products/categories/18'}
        ],
       'collection': [
         {'href': 'http://localhost/wp-json/wc/v3/products/categories'}
        ]
      }
    },
    {
      'id': 19,
      'name': 'New category 2',
      'slug': 'new-category-2',
      'parent': 0,
      'description': 'Second new category',
      'display': 'default',
      'image': None,
      'menu_order': 0,
      'count': 0,
      '_links': {
        'self': [
          {'href': 'http://localhost/wp-json/wc/v3/products/categories/19'}
        ],
        'collection': [
          {'href': 'http://localhost/wp-json/wc/v3/products/categories'}
        ]
      }
    }
  ],
  'update': [
    {
      'id': 17,
      'name': 'Example category',
      'slug': 'example-category',
      'parent': 0,
      'description': 'updated description',
      'display': 'default',
      'image': None,
      'menu_order': 0,
      'count': 0,
      '_links': {
        'self': [
          {'href': 'http://localhost/wp-json/wc/v3/products/categories/17'}
        ],
        'collection': [
          {'href': 'http://localhost/wp-json/wc/v3/products/categories'}
        ]
      }
    }
  ],
  'delete': [
    {
      'id': 16,
      'name': 'test',
      'slug': 'test',
      'parent': 0,
      'description': 'a test',
      'display': 'default',
      'image': None,
      'menu_order': 0,
      'count': 0,
      '_links': {
        'self': [
          {'href': 'http://localhost/wp-json/wc/v3/products/categories/16'}
        ],
        'collection': [
          {'href': 'http://localhost/wp-json/wc/v3/products/categories'}
        ]
      }
    }
  ]
}

Products

Until now we saw how to perform the basic CRUD operations on categories. Let’s work with products now. The code that should be used is pretty similar; what changes, of course, are the API endpoints and the attributes that should
be used when creating a product.

Creating a simple product

The first example we will see is how to create a “simple” product, without variations (variations are slightly different versions of the same product, based for example on different sizes or colors). First we define the product
data:

product_data = {
    "name": "Simple example product",
    "type": "simple",
    "regular_price": "22.50",
    "stock_quantity": 10,
    "short_description": "just an example product",
    "description": "This is just an example product, created with the Woocommerce REST API",
    "categories": [
      {
        "id": 17
      }
    ],

    "images": [
        {
            "src": "https://linuxconfig.org/images/linuxconfig_logo.png",
            "alt": "example-image"
        }
    ]
}

Let’s take a look at the product information we used inside the product_data dictionary. We defined the product name (Simple example product), then we specified its type, which in this case is “simple”, since we are creating the listing for a physical product without variations. Both those information must be specified as strings.

We also specified the product regular_price (string), the stock quantity (integer), the short_description and the regular description, both as strings: those are displayed in different parts of the page when the product
is visualized by a potential customer.

The next thing we did was to specify the list of product categories the product should be included under. Each category should be referenced by its id (integer). In this case we just referenced the category with 17 as unique
identifier (“Example category”).

The last thing we defined, was the list of the images which should be associated with the product. Each image is described using a dictionary. Here we just used one image, providing its src (string), and alt (the alternative text, as
a string).

The one we used are only a very small subset of all the possible product properties. Once our data is ready, we send a POST HTTP request, using the post method of the wcapi object. The endpoint to which the request should be sent is
“products”:

response = wcapi.post('products', product_data)

If the request is successful, by executing response.json() we will obtain a Python dictionary containing the information of the newly created product:

{
  'id': 29,
  'name': 'Simple example product',
  'slug': 'simple-example-product',
  'permalink': 'http://localhost/product/simple-example-product/',
  'date_created': '2021-03-22T14:53:44',
  'date_created_gmt': '2021-03-22T14:53:44',
  'date_modified': '2021-03-22T14:53:44',
  'date_modified_gmt': '2021-03-22T14:53:44',
  'type': 'simple',
  'status': 'publish',
  'featured': False,
  'catalog_visibility': 'visible',
  'description': 'This is just an example product, created with the Woocommerce REST API',
  'short_description': 'just an example product',
  'sku': '',
  'price': '22.50',
  'regular_price': '22.50',
  'sale_price': '',
  'date_on_sale_from': None,
  'date_on_sale_from_gmt': None,
  'date_on_sale_to': None,
  'date_on_sale_to_gmt': None,
  'on_sale': False,
  'purchasable': True,
  'total_sales': 0,
  'virtual': False,
  'downloadable': False,
  'downloads': [],
  'download_limit': -1,
  'download_expiry': -1,
  'external_url': '',
  'button_text': '',
  'tax_status': 'taxable',
  'tax_class': '',
  'manage_stock': False,
  'stock_quantity': None,
  'backorders': 'no',
  'backorders_allowed': False,
  'backordered': False,
  'sold_individually': False,
  'weight': '',
  'dimensions': {
    'length': '',
    'width': '',
    'height': ''
  },
  'shipping_required': True,
  'shipping_taxable': True,
  'shipping_class': '',
  'shipping_class_id': 0,
  'reviews_allowed': True,
  'average_rating': '0',
  'rating_count': 0,
  'upsell_ids': [],
  'cross_sell_ids': [],
  'parent_id': 0,
  'purchase_note': '',
  'categories': [
    {
      'id': 17,
      'name': 'Example category',
      'slug': 'example-category'
    }
  ],
  'tags': [],
  'images': [
    {
      'id': 28,
      'date_created': '2021-03-22T14:53:44',
      'date_created_gmt': '2021-03-22T14:53:44',
      'date_modified': '2021-03-22T14:53:44',
      'date_modified_gmt': '2021-03-22T14:53:44',
      'src': 'http://localhost/wp-content/uploads/2021/03/linuxconfig_logo-3.png',
      'name': 'linuxconfig_logo-3.png',
      'alt': 'example-image'
    }
  ],
  'attributes': [],
  'default_attributes': [],
  'variations': [],
  'grouped_products': [],
  'menu_order': 0,
  'price_html': '<span class="woocommerce-Price-amount amount"><bdi>22,50<span class="woocommerce-Price-currencySymbol">&euro;</span></bdi></span>',
  'related_ids': [],
  'meta_data': [],
  'stock_status': 'instock',
  '_links': {
    'self': [
      {'href': 'http://localhost/wp-json/wc/v3/products/29'}
    ],
    'collection': [
      {'href': 'http://localhost/wp-json/wc/v3/products'}
    ]
  }
}

Creating a variable product and its variations

In the previous example we created a “simple” product. Now let’s see how to create a “variable” product. The basic definition is the same as the one we used above. All we have to do is to add the attributes which, combined, will represent the product variations. For example, suppose our product is a t-shirt available in multiple colors and sizes: each variation will be composed by a certain color associated with a certain size:

variable_product_data = {
  "name": "Variable example product",
  "type": "variable",
  "short_description": "just a variable product",
  "description": "This is a variable product, created with the Woocommerce REST API",
  "categories": [
    {
      "id": 17
    }
  ],
  "images": [
      {
          "src": "https://linuxconfig.org/images/linuxconfig_logo.png",
          "alt": "example-image"
      }
  ],
  "attributes": [
    {
      "name": "Color",
      "visible": True,
      "variation": True,
      "options": [
        "black",
        "white"
      ]
    },
    {
      "name": "Size",
      "visible": True,
      "variation": True,
      "options": [
        "S",
        "M",
        "L"
      ]
    }
  ]
}

We provided a list of attributes using the “attributes” key in the product dictionary. The first attribute is named “Color”, and the second “Size”. We set both of them to be visible, and we declared that they should be used for variations by assigning True as the value of the variation key in the dictionaries that defines them. The list associated with the options key, contains all the possible values each attributes could assume.

About a product attributes: we can use both global and non-global attributes. What is the difference? Attributes specified only by name as we did in the example above will be created “on the fly” for the specific product, so they will be “non-global”. If we meant to use the same attribute for several products, we better define it beforehand with a specific call, and then reference it by their id. Supposing we created the Color and Size global attributes, and they have respectively 1 and 2 as unique identifiers, we would write:

"attributes": [
    {
      "id": 1
      "visible": True,
      "variation": True,
      "options": [
        "black",
        "white"
      ]
    },
    {
      "id": 2,
      "visible": True,
      "variation": True,
      "options": [
        "S",
        "M",
        "L"
      ]
    }
]


Very important: the code will not create the actual product variations, which should be defined with separate requests.

You can notice that we omitted to provide the product stock quantity, since the total stock value will be represented by the sum of the stock quantity of each variation.

The next step is to create the actual product variations. When defining the variations data, we should use the same attributes we defined in the request we used to create the variable product. Here is an example on how to create a variation:

product_variation_data = {
    "regular_price": "18.00",
    "stock_quantity": 10,
    "attributes": [
        {
          "id": 1,
          "option": "white"
        },
        {
          "id": 2,
          "option": "S"
        }
    ]
}

We defined the variation produced by the combination of the “white” Color attribute, and the “S” Size attribute. We set its regular_price and stock_quantity. We should now send the request with the following code:

response = wcapi.post('products/34/variations', product_variation_data)

As you can see in the snippet above, as endpoint for the request, we used products/34/variations, where 34 is the id of the parent product we previously created.

Requesting information about all products or a specific one

Just as we did for categories, we can request information about all existing products via the Woocommerce REST API:

response = wcapi.get('products')

The request could be further customized by the use of parameters: with the per_page parameter, for example, we can specify how many items should be returned in the result set of a single request (default is 10), and with the page parameter we can request the specific page that should be returned (default is 1). To extend the number of items returned in a single request, we would write:

response = wcapi.get('products', params={'per_page': 20})

Requesting information about a specific product it’s just as simple: all we have to do is to specify its id in the request endpoint:

response = wcapi.get('products/34')

Updating a product

The logic used to update a product is the same we used to update a category (and all other “entities” managed via REST API). We specify the product data that should be updated and send a PUT request to the endpoint which includes the product id:

updated_product_data = {
  "description": "This is the updated variable product description"
}

response = wcapi.put('products/34', updated_product_data)

Deleting a product

To delete a product, all we have to do is to send a DELETE request to the endpoint which includes the product id:

response = wcapi.delete('products/34')

Here you can notice that, unlike what happens for categories, the use of the force parameter is not mandatory. If the parameter is not used the product will be just moved to the “Trash”, and so it will be possible to retrieve it from it lately. If the force parameter is used, and set to True, instead, the product will be permanently removed.

Conclusions

In this tutorial we saw some examples of how to interact with the Woocommerce REST API with the Python programming language. We saw how to generate API credentials, how to install the “woocommerce” Python package which provides useful methods that simplify the code we should write to interact with them, and some examples of how to create, read, delete and update Woocommerce categories and products. Please consult the official Woocommerce REST API documentation for a complete overview of the API capabilities.



Comments and Discussions
Linux Forum