Calling the Magento API from Python


Here are the results of an evaluation I did recently before implementing a product feed from the ChainDrive retail management system into Magento using Python. It should prove useful for anyone considering using Python as a web services client to Magento.

Magento has three web services APIs: SOAP, XML-RPC and REST. The evaluation was performed to determine which of those APIs would be most suitable for the purpose.

The code snippets below are those used to do a quick evaluation of the performance of a simple update of a product name in Magento. Each snippet assumes you have a product with an ID of 10.

Comparative Performance

Lets get to the meat! Here's the comparative performance of each of the APIs. The first timing is for setting up the session. The second is the time to complete 20 product title updates.

API Type Setup Session 20 product updates
SOAP 16.31 Sec 175.17 Sec
XML-RPC 0.49 Sec 19.50 Sec
REST 1.51e-05 Sec 21.77 Sec

You'd expect the SOAP API to be slower due to it's nature of being strictly typed and overheads associated with WSDL processing .It certainly lived up to that expectation.

SOAP API

Code

For the SOAP API test, the SOAPpy library was used with the following code:

import datetime
import timeit

import SOAPpy

client = None
session = None


def setup():
    global client, session
    # your domain here
    client = SOAPpy.WSDL.Proxy("http://yourdomain.com/index.php/api/v2_soap?wsdl=1&type=soap")
    # your credentials here
    session = client.login('gateway', '1nx0fqeqs2h8sd896npeya57mch9py04')


def test_update():
    global client, session
    name = 'Blackberry Playbook 7 WiFi Tablet - 64GB ' + \
           str(datetime.datetime.now())
    result = client.catalogProductUpdate(session, '10', {'name': name}, 0, 'id')
    print(result)


if __name__ == '__main__':
    print('Session Setup:')
    print(timeit.timeit('setup()',
                        setup='from __main__ import setup',
                        number=1))
    print('Updates')
    print(timeit.timeit('test_update()',
                        setup='from __main__ import test_update',
                        number=20))

Summary

  • Extremely slow.
  • Requires third-party SOAP package.
  • Easy to setup
  • Supports product queries using SKU.

XML-RPC API

Code

import timeit
import xmlrpclib
import datetime

client = None
session = None


def setup():
    global client, session
    # your domain here
    client = xmlrpclib.ServerProxy("http://yourdomain.com/index.php/api/xmlrpc?type=xmlrpc")
    # your credentials here
    session = client.login('gateway', '1nx0fqeqs2h8sd896npeya57mch9py04')


def test_update():
    global client, session
    name = 'Blackberry Playbook 7" WiFi Tablet - 64GB ' + \
           str(datetime.datetime.now())
    params = ['10', {'name': name}, 0, 'id']
    response = client.call(session, 'catalog_product.update', params)
    print(response)


if __name__ == '__main__':
    print('Session Setup:')
    print(timeit.timeit('setup()',
                        setup='from __main__ import setup',
                        number=1))
    print('Updates')
    print(timeit.timeit('test_update()',
                        setup='from __main__ import test_update',
                        number=20))

Summary

  • Reasonably fast.
  • XML-RPC support is built into Python.
  • Easy to setup
  • Supports product queries using SKU

REST

Code

import json
import timeit
import datetime

import oauth2 as oauth

# oauth constants - enter yours here
CONSUMER_KEY = 'ee9kqzv9ls8cl9lwoqjjisxq1988pzaf'
CONSUMER_SECRET = 'oba6t6b7pww35qeq61qq4nrbdbjh6bti'
REQUEST_TOKEN = 'nh0bzbc0toxzjyn9d7yo84qchf904uq2'
TOKEN = 'zf6l21glkg006sbnb324qhnlxcmi5ajz'
SECRET = 'pbwyxnetzb27gkowu7fm7btj6a7prjcy'
# your domain here
API_URL = 'http://youdomain.com/api/rest'

client = None


def setup():
    global client
    consumer = oauth.Consumer(key=CONSUMER_KEY, secret=CONSUMER_SECRET)
    token = oauth.Token(key=TOKEN, secret=SECRET)

    client = oauth.Client(consumer, token)
    pass


def test_update():
    global client
    name = 'Blackberry Playbook 7" WiFi Tablet - 64GB ' + \
           str(datetime.datetime.now())
    data = {'name': name}
    headers = {'Accept': '*/*', 'Content-Type': 'application/json'}
    resp, content = client.request(
        API_URL + '/products/10?type=rest',
        method='PUT', body=json.dumps(data), headers=headers)
    print(resp.status)


if __name__ == '__main__':
    print('Session Setup:')
    print(timeit.timeit('setup()',
                        setup='from __main__ import setup',
                        number=1))
    print('Updates')
    print(timeit.timeit('test_update()',
                        setup='from __main__ import test_update',
                        number=20))

Summary

  • Reasonably fast.
  • Limited web services available compared to SOAP & XML-RPC
  • Magento REST web services API is not extensible (should you need to extend it).
  • Third-party Oauth package required.
  • Oauth, used in a headless app, is painful to setup initially but works well once you get there.
  • Cannot query/update products using SKU. You need to know Magento's internal product id which is a killer for implementing an external product feed

How did it go?

The product feed was implemented using the XML-RPC API and has been running successfully with a product base of around 12,000 products. On a modest VPS account, the product feed takes about 1.5 hours to run.

The feed functions as follows:

  • Read all products from Magento into memory.
  • If the product from retail management system does not exist in Magento, create a basic product in Magento in a a disabled state.
  • If the Magento field values differ from those in the retail management system, update the fields.