Products

Listing all Products

A GET request to /api/products/ is used to list all products.

Using count and offset restricts the number of returned products.

Parameter Description
count Limit the number of results. This can improve performance.
offset The start position of results, used for paging result sets. Can be used with count parameter.
"""
List all products using the OnSite API.
"""
import requests
from xml.dom import minidom

# Customize these to your install.
ONSITE_HOST = 'localhost'
ONSITE_PORT = 9630
ONSITE_USERNAME = 'lightspeed'
ONSITE_PASSWORD = 'admin'
APP_ID = 'com.lightspeed.onsite.demo'
APP_VERSION = '1.0'
APP_PRIVATE_ID = '12345678-90ab-cdef-1234-567890abcdef'

products_url = 'https://%s:%d/api/products/?count=10&offset=0' % (ONSITE_HOST, ONSITE_PORT)
logout_url = 'https://%s:%d/api/sessions/current/logout/' % (ONSITE_HOST, ONSITE_PORT)

# Setup a session for the http request.
session = requests.Session()
session.auth = (ONSITE_USERNAME, ONSITE_PASSWORD)
session.headers.update({
    'user-agent': '%s/%s' % (APP_ID, APP_VERSION),
    'x-pappid': APP_PRIVATE_ID})
session.verify = False
session.stream = True

# Send the request to list all products.
get_response = session.get(products_url)
assert get_response.status_code == 200

# Log out.
logout_response = session.post(logout_url)
assert logout_response.status_code == 204

# Get the response and print it.
response_xml = minidom.parseString(get_response.text)
print(response_xml.toprettyxml())
<!-- Response -->

<?xml version="1.0" ?>
<products total_count="2">
   <product full_render="false" id="31" uri="https://localhost:9630/api/products/31/">
       <code>LEATHER-JACKET</code>
       <flags>
           <inventoried>false</inventoried>
           <editable_sell>false</editable_sell>
           <master_model>false</master_model>
       </flags>
       <sell_price>159.990</sell_price>
       <description>A beautiful leather jacket.</description>
       <inventory>
           <available>-3.000</available>
           <reserved>0.000</reserved>
           <coming_for_stock>0</coming_for_stock>
           <coming_for_customer>0</coming_for_customer>
           <warehouses>0.000</warehouses>
           <in_transit>0.000</in_transit>
           <total>-3.000</total>
       </inventory>
       <product_photos/>
   </product>
   <product full_render="false" id="36" uri="https://localhost:9630/api/products/36/">
       <code>BLACK-PURSE</code>
       <flags>
           <inventoried>false</inventoried>
           <editable_sell>false</editable_sell>
           <master_model>false</master_model>
       </flags>
       <sell_price>59.990</sell_price>
       <description>A beautiful black purse.</description>
       <inventory>
           <available>-3.000</available>
           <reserved>0.000</reserved>
           <coming_for_stock>0</coming_for_stock>
           <coming_for_customer>0</coming_for_customer>
           <warehouses>0.000</warehouses>
           <in_transit>0.000</in_transit>
           <total>-3.000</total>
       </inventory>
       <product_photos/>
   </product>
</products>

Warning

This request can time-out if there are too many products to return. Use limit and offset parameters to mitigate this.

Note

It is recommended to search products rather than to list them all. See Searching Products.

Creating a Product

A POST request to /api/products/ is used to create a products.

The following script creates a product with the OnSite API. Here the product is distilled at its simplest: the product code, the description of the product, and a price. This example will create a new product, with a product code of TEST-PRODUCT, a description of API Test Product and a sell price of $9.99.

There are other fields that can be set, however they can be left out if you don’t want to set them. Some fields will have default values set, depending on the setup in OnSite. For example, the Currency will always be set to the server’s default currency.

"""
Create a product using the OnSite API.
"""
import requests
from xml.dom import minidom

# Customize these to your install.
ONSITE_HOST = 'localhost'
ONSITE_PORT = 9630
ONSITE_USERNAME = 'lightspeed'
ONSITE_PASSWORD = 'admin'
APP_ID = 'com.lightspeed.onsite.demo'
APP_VERSION = '1.0'
APP_PRIVATE_ID = '12345678-90ab-cdef-1234-567890abcdef'

products_url = 'https://%s:%d/api/products/' % (ONSITE_HOST, ONSITE_PORT)
logout_url = 'https://%s:%d/api/sessions/current/logout/' % (ONSITE_HOST, ONSITE_PORT)

# This is the product that will be created.
product_xml = """
<product>
    <code>TEST-PRODUCT</code>
    <description>API Test Product</description>
    <sells>
        <sell>9.99</sell>
    </sells>
</product>
"""

# Create a session. This will persist cookies across all requests.
session = requests.Session()
session.auth = (ONSITE_USERNAME, ONSITE_PASSWORD)
session.headers.update({
    'user-agent': '%s/%s' % (APP_ID, APP_VERSION),
    'x-pappid': APP_PRIVATE_ID})
session.verify = False

# Send the request to create the product.
post_response = session.post(products_url, data=product_xml)
assert post_response.status_code == 201

# Log out.
logout_response = session.post(logout_url)
assert logout_response.status_code == 204

# Get the response and print it.
response_xml = minidom.parseString(post_response.text)
print(response_xml.toprettyxml())
<!-- Response -->

<?xml version="1.0" ?>
<product full_render="true" id="64" uri="https://localhost:9630/api/products/64/">
   <class/>
   <currency id="1" uri="https://localhost:9630/api/setup/currencies/1/"/>
   <code>TEST-PRODUCT</code>
   <costs>
       <cost/>
       <average>0</average>
       <raw/>
   </costs>
   <supplier_costs/>
   <flags>
       <current>true</current>
       <editable>false</editable>
       <gift_card>false</gift_card>
       <inventoried>false</inventoried>
       <new_cost>false</new_cost>
       <new_import>false</new_import>
       <new_update>false</new_update>
       <no_live_rules>false</no_live_rules>
       <no_profit>false</no_profit>
       <serialized>false</serialized>
       <web>false</web>
       <editable_sell>false</editable_sell>
       <master_model>false</master_model>
   </flags>
   <sell_price>9.99</sell_price>
   <pricing_levels/>
   <created>2017-07-19T15:06:33.684995</created>
   <modified>2017-07-19T15:06:33.685328</modified>
   <description>API Test Product</description>
   <long_web_description/>
   <family/>
   <gl_product>
       <asset/>
       <cogs_expense/>
       <income/>
       <payable_expense/>
   </gl_product>
   <product_id>P-1063</product_id>
   <import_id/>
   <inventory>
       <available>0</available>
       <reserved>0</reserved>
       <coming_for_stock>0</coming_for_stock>
       <coming_for_customer>0</coming_for_customer>
       <warehouses>0</warehouses>
       <in_transit>0</in_transit>
       <total>0</total>
   </inventory>
   <margin/>
   <minimum_margin>0</minimum_margin>
   <notes/>
   <product_info>
       <color/>
       <height>0</height>
       <length>0</length>
       <size/>
       <weight>0.000</weight>
       <width>0.000</width>
   </product_info>
   <reorder>
       <amount>0</amount>
       <calc>0</calc>
       <point>0</point>
       <type>0</type>
   </reorder>
   <sells>
       <sell>9.99</sell>
       <sell_tax_inclusive/>
       <sell_web/>
   </sells>
   <supplier/>
   <supplier_code/>
   <upc/>
   <web>
       <inventory/>
   </web>
   <keywords>
       <keyword/>
       <keyword/>
       <keyword/>
   </keywords>
   <multi_store_label/>
   <multi_store_master_label/>
   <categories>
       <pos/>
       <web/>
   </categories>
   <related_products/>
   <serial_numbers/>
   <product_photos/>
   <tax_exemption id="0" uri="https://localhost:9630/api/setup/tax_exemptions/0/">
       <name>Default</name>
       <taxes>
           <tax id="1">
               <exempt>false</exempt>
           </tax>
           <tax id="2">
               <exempt>false</exempt>
           </tax>
           <tax id="3">
               <exempt>false</exempt>
           </tax>
           <tax id="4">
               <exempt>false</exempt>
           </tax>
           <tax id="5">
               <exempt>false</exempt>
           </tax>
       </taxes>
       <active>true</active>
   </tax_exemption>
   <master_product/>
</product>

Getting a Single Product

A GET request to /api/products/{id}/ is used to get a single product.

"""
Get a product using the OnSite API.
"""
import requests
from xml.dom import minidom

# Customize these to your install.
ONSITE_HOST = 'localhost'
ONSITE_PORT = 9630
ONSITE_USERNAME = 'lightspeed'
ONSITE_PASSWORD = 'admin'
APP_ID = 'com.lightspeed.onsite.demo'
APP_VERSION = '1.0'
APP_PRIVATE_ID = '12345678-90ab-cdef-1234-567890abcdef'

product_id = 68
product_url = 'https://%s:%d/api/products/%d/' % (ONSITE_HOST, ONSITE_PORT, product_id)
logout_url = 'https://%s:%d/api/sessions/current/logout/' % (ONSITE_HOST, ONSITE_PORT)

# Setup a session for the http request.
session = requests.Session()
session.auth = (ONSITE_USERNAME, ONSITE_PASSWORD)
session.headers.update({
    'user-agent': '%s/%s' % (APP_ID, APP_VERSION),
    'x-pappid': APP_PRIVATE_ID})
session.verify = False
session.stream = True

# Send the request to get a product.
get_response = session.get(product_url)
assert get_response.status_code == 200

# Log out.
logout_response = session.post(logout_url)
assert logout_response.status_code == 204

# Get the response and print it.
response_xml = minidom.parseString(get_response.text)
print(response_xml.toprettyxml())
<!-- Response -->

<?xml version="1.0" ?>
<product full_render="true" id="68" uri="https://localhost:9630/api/products/68/">
   <class id="9" uri="https://localhost:9630/api/setup/classes/9/"/>
   <currency id="1" uri="https://localhost:9630/api/setup/currencies/1/"/>
   <code>LMBRJNS-COMIC-VOL-1</code>
   <costs>
       <cost/>
       <average>0.000</average>
       <raw/>
   </costs>
   <supplier_costs/>
   <flags>
       <current>true</current>
       <editable>true</editable>
       <gift_card>false</gift_card>
       <inventoried>true</inventoried>
       <new_cost>false</new_cost>
       <new_import>false</new_import>
       <new_update>false</new_update>
       <no_live_rules>false</no_live_rules>
       <no_profit>false</no_profit>
       <serialized>false</serialized>
       <web>false</web>
       <editable_sell>false</editable_sell>
       <master_model>false</master_model>
   </flags>
   <sell_price>19.990</sell_price>
   <pricing_levels/>
   <created>2017-07-20T08:49:44.537147</created>
   <modified>2017-07-20T08:49:44.537147</modified>
   <description>Lumberjanes Comic Vol. 1</description>
   <long_web_description/>
   <family/>
   <gl_product>
       <asset/>
       <cogs_expense/>
       <income/>
       <payable_expense/>
   </gl_product>
   <product_id>P-1067</product_id>
   <import_id/>
   <inventory>
       <available>8.000</available>
       <reserved>0.000</reserved>
       <coming_for_stock>0</coming_for_stock>
       <coming_for_customer>0</coming_for_customer>
       <warehouses>0.000</warehouses>
       <in_transit>0.000</in_transit>
       <total>8.000</total>
   </inventory>
   <margin/>
   <minimum_margin>0.000</minimum_margin>
   <notes/>
   <product_info>
       <color/>
       <height>0.000</height>
       <length>0.000</length>
       <size/>
       <weight>0.000</weight>
       <width>0.000</width>
   </product_info>
   <reorder>
       <amount>0.000</amount>
       <calc>0.000</calc>
       <point>0.000</point>
       <type>0</type>
   </reorder>
   <sells>
       <sell>19.990</sell>
       <sell_tax_inclusive/>
       <sell_web/>
   </sells>
   <supplier id="1" uri="https://localhost:9630/api/suppliers/1/"/>
   <supplier_code/>
   <upc/>
   <web>
       <inventory/>
   </web>
   <keywords>
       <keyword/>
       <keyword/>
       <keyword/>
   </keywords>
   <multi_store_label>68</multi_store_label>
   <multi_store_master_label/>
   <categories>
       <pos/>
       <web/>
   </categories>
   <related_products/>
   <serial_numbers/>
   <product_photos/>
   <tax_exemption id="0" uri="https://localhost:9630/api/setup/tax_exemptions/0/">
       <name>Default</name>
       <taxes>
           <tax id="1">
               <exempt>false</exempt>
           </tax>
           <tax id="2">
               <exempt>false</exempt>
           </tax>
           <tax id="3">
               <exempt>false</exempt>
           </tax>
           <tax id="4">
               <exempt>false</exempt>
           </tax>
           <tax id="5">
               <exempt>false</exempt>
           </tax>
       </taxes>
       <active>true</active>
   </tax_exemption>
   <master_product/>
</product>

Updating a Product

A PUT request to /api/products/{id}/ is used to change the details of a product.

This must be preceded by a LOCK request to the same endpoint, and followed by an UNLOCK request. See Locking & Unlocking Resources.

"""
Update a product using the OnSite API.
"""
import requests
from xml.dom import minidom

# Customize these to your install.
ONSITE_HOST = 'localhost'
ONSITE_PORT = 9630
ONSITE_USERNAME = 'lightspeed'
ONSITE_PASSWORD = 'admin'
APP_ID = 'com.lightspeed.onsite.demo'
APP_VERSION = '1.0'
APP_PRIVATE_ID = '12345678-90ab-cdef-1234-567890abcdef'

product_id = 66
product_url = 'https://%s:%d/api/products/%d/' % (ONSITE_HOST, ONSITE_PORT, product_id)
logout_url = 'https://%s:%d/api/sessions/current/logout/' % (ONSITE_HOST, ONSITE_PORT)

# This is the updated information for the product.
product_xml = """
<product>
    <sells>
        <sell>14.99</sell>
    </sells>
</product>
"""

# Create a session. This will persist cookies across all requests.
session = requests.Session()
session.auth = (ONSITE_USERNAME, ONSITE_PASSWORD)
session.headers.update({
    'user-agent': '%s/%s' % (APP_ID, APP_VERSION),
    'x-pappid': APP_PRIVATE_ID})
session.verify = False

# Lock the product before making the change.
lock_response = session.request('LOCK', product_url)
assert lock_response.status_code == 200

# Send the request to create the product.
put_response = session.put(product_url, data=product_xml)
assert put_response.status_code == 200

# Unlock the product after the change is complete.
unlock_response = session.request('UNLOCK', product_url)
assert unlock_response.status_code == 200

# Log out.
logout_response = session.post(logout_url)
assert logout_response.status_code == 204

# Print the revised product.
response_xml = minidom.parseString(put_response.text)
print(response_xml.toprettyxml())
<!-- Response -->

<?xml version="1.0" ?>
<product full_render="true" id="66" uri="https://localhost:9630/api/products/66/">
   <class/>
   <currency id="1" uri="https://localhost:9630/api/setup/currencies/1/"/>
   <code>TEST-PRODUCT</code>
   <costs>
       <cost/>
       <average>0.000</average>
       <raw/>
   </costs>
   <supplier_costs/>
   <flags>
       <current>true</current>
       <editable>false</editable>
       <gift_card>false</gift_card>
       <inventoried>false</inventoried>
       <new_cost>false</new_cost>
       <new_import>false</new_import>
       <new_update>false</new_update>
       <no_live_rules>false</no_live_rules>
       <no_profit>false</no_profit>
       <serialized>false</serialized>
       <web>false</web>
       <editable_sell>false</editable_sell>
       <master_model>false</master_model>
   </flags>
   <sell_price>14.99</sell_price>
   <pricing_levels/>
   <created>2017-07-19T15:35:11.466879</created>
   <modified>2017-07-20T09:33:41.453508</modified>
   <description>API Test Product</description>
   <long_web_description/>
   <family/>
   <gl_product>
       <asset/>
       <cogs_expense/>
       <income/>
       <payable_expense/>
   </gl_product>
   <product_id>P-1065</product_id>
   <import_id/>
   <inventory>
       <available>0.000</available>
       <reserved>0.000</reserved>
       <coming_for_stock>0</coming_for_stock>
       <coming_for_customer>0</coming_for_customer>
       <warehouses>0.000</warehouses>
       <in_transit>0.000</in_transit>
       <total>0.000</total>
   </inventory>
   <margin/>
   <minimum_margin>0.000</minimum_margin>
   <notes/>
   <product_info>
       <color/>
       <height>0.000</height>
       <length>0.000</length>
       <size/>
       <weight>0.000</weight>
       <width>0.000</width>
   </product_info>
   <reorder>
       <amount>0.000</amount>
       <calc>0.000</calc>
       <point>0.000</point>
       <type>0</type>
   </reorder>
   <sells>
       <sell>14.99</sell>
       <sell_tax_inclusive/>
       <sell_web/>
   </sells>
   <supplier/>
   <supplier_code/>
   <upc/>
   <web>
       <inventory/>
   </web>
   <keywords>
       <keyword/>
       <keyword/>
       <keyword/>
   </keywords>
   <multi_store_label>66</multi_store_label>
   <multi_store_master_label/>
   <categories>
       <pos/>
       <web/>
   </categories>
   <related_products/>
   <serial_numbers/>
   <product_photos/>
   <tax_exemption id="0" uri="https://localhost:9630/api/setup/tax_exemptions/0/">
       <name>Default</name>
       <taxes>
           <tax id="1">
               <exempt>false</exempt>
           </tax>
           <tax id="2">
               <exempt>false</exempt>
           </tax>
           <tax id="3">
               <exempt>false</exempt>
           </tax>
           <tax id="4">
               <exempt>false</exempt>
           </tax>
           <tax id="5">
               <exempt>false</exempt>
           </tax>
       </taxes>
       <active>true</active>
   </tax_exemption>
   <master_product/>
</product>

Listing Product Photos

A GET request to /api/products/{product id}/product_photos/ is used to list product photos.

"""
List all product photos using the OnSite API.
"""
import requests
from xml.dom import minidom

# Customize these to your install.
ONSITE_HOST = 'localhost'
ONSITE_PORT = 9630
ONSITE_USERNAME = 'lightspeed'
ONSITE_PASSWORD = 'admin'
APP_ID = 'com.lightspeed.onsite.demo'
APP_VERSION = '1.0'
APP_PRIVATE_ID = '12345678-90ab-cdef-1234-567890abcdef'

product_id = 67
product_photos_url = 'https://%s:%d/api/products/%d/product_photos/' % (ONSITE_HOST, ONSITE_PORT, product_id)
logout_url = 'https://%s:%d/api/sessions/current/logout/' % (ONSITE_HOST, ONSITE_PORT)

# Setup a session for the http request.
session = requests.Session()
session.auth = (ONSITE_USERNAME, ONSITE_PASSWORD)
session.headers.update({
    'user-agent': '%s/%s' % (APP_ID, APP_VERSION),
    'x-pappid': APP_PRIVATE_ID})
session.verify = False
session.stream = True

# Send the request to list all photos for a product.
get_response = session.get(product_photos_url)
assert get_response.status_code == 200

# Log out.
logout_response = session.post(logout_url)
assert logout_response.status_code == 204

# Get the response and print it.
response_xml = minidom.parseString(get_response.text)
print(response_xml.toprettyxml())
<!-- Response -->

<?xml version="1.0" ?>
<product_photos>
   <product_photo id="3" uri="https://localhost:9630/api/products/67/product_photos/3/">
       <sort_order>0</sort_order>
       <scales>
           <scale>
               <filename>front.jpg</filename>
               <size>original</size>
               <format>JPEG</format>
           </scale>
           <scale>
               <filename>front_256.jpeg</filename>
               <size>256</size>
               <format>JPEG</format>
           </scale>
           <scale>
               <filename>front_512.jpeg</filename>
               <size>512</size>
               <format>JPEG</format>
           </scale>
       </scales>
   </product_photo>
   <product_photo id="4" uri="https://localhost:9630/api/products/67/product_photos/4/">
       <sort_order>1</sort_order>
       <scales>
           <scale>
               <filename>back.jpg</filename>
               <size>original</size>
               <format>JPEG</format>
           </scale>
           <scale>
               <filename>back_256.jpeg</filename>
               <size>256</size>
               <format>JPEG</format>
           </scale>
           <scale>
               <filename>back_512.jpeg</filename>
               <size>512</size>
               <format>JPEG</format>
           </scale>
       </scales>
   </product_photo>
</product_photos>

Adding a Product Photo

A POST request to /api/products/{id}/add_product_photo/ is used to add a product photo.

When an image is added to a product in OnSite, two additional sizes are created and saved, one resized to fit in a 512 pixel square, and the other a 256 pixel square.

This must be preceded by a LOCK request to the same endpoint, and followed by an UNLOCK request. See Locking & Unlocking Resources.

An additional header, Content-Location is used to identify the filename for the photo on the server.

"""
Add a product photo using the OnSite API.
"""
import requests
from xml.dom import minidom

# Customize these to your install.
ONSITE_HOST = 'localhost'
ONSITE_PORT = 9630
ONSITE_USERNAME = 'lightspeed'
ONSITE_PASSWORD = 'admin'
APP_ID = 'com.lightspeed.onsite.demo'
APP_VERSION = '1.0'
APP_PRIVATE_ID = '12345678-90ab-cdef-1234-567890abcdef'

photo_file_path = 'examples/sample-photo.jpg'
photo_name_on_server = 'product-photo-1.jpg'
product_id = 67
product_url = 'https://%s:%d/api/products/%d/' % (ONSITE_HOST, ONSITE_PORT, product_id)
logout_url = 'https://%s:%d/api/sessions/current/logout/' % (ONSITE_HOST, ONSITE_PORT)

# Load a photo.
with open(photo_file_path, 'rb') as photo:
    
    # Create a session. This will persist cookies across all requests.
    session = requests.Session()
    session.auth = (ONSITE_USERNAME, ONSITE_PASSWORD)
    session.headers.update({
        'user-agent': '%s/%s' % (APP_ID, APP_VERSION),
        'x-pappid': APP_PRIVATE_ID})
    session.verify = False
    
    # Lock the product before making the change.
    lock_response = session.request('LOCK', product_url)
    assert lock_response.status_code == 200
    
    # Send the request to add a photo to a product.
    post_response = session.post(product_url + 'add_product_photo/', data=photo, headers={
        'content-location': photo_name_on_server
    })
    assert post_response.status_code == 201
    
    # Unlock the product after the change is complete.
    unlock_response = session.request('UNLOCK', product_url)
    assert unlock_response.status_code == 200

    # Log out.
    logout_response = session.post(logout_url)
    assert logout_response.status_code == 204
    
    # Get the response and print it.
    response_xml = minidom.parseString(post_response.text)
    print(response_xml.toprettyxml())
<!-- Response -->

<?xml version="1.0" ?>
<product_photo id="6" uri="https://localhost:9630/api/products/67/product_photos/6/">
   <sort_order>3</sort_order>
   <scales>
       <scale>
           <filename>product-photo-1_512.jpg</filename>
           <size>512</size>
           <format>JPEG</format>
       </scale>
       <scale>
           <filename>product-photo-1_256.jpg</filename>
           <size>256</size>
           <format>JPEG</format>
       </scale>
       <scale>
           <filename>product-photo-1.jpg</filename>
           <size>original</size>
           <format>JPEG</format>
       </scale>
   </scales>
</product_photo>

Note

There is a maximum of 12 photos per product.

Downloading a Product Photo

A GET request to /api/products/{product id}/product_photos/{photo id}/image/ is used to get a product photo.

An additional header, Accept, is used to identify the desired file size. The options are:

  • image/*;size=256
  • image/*;size=512
  • image/*;size=original
"""
Get a product photo using the OnSite API.
"""
import requests
from xml.dom import minidom

# Customize these to your install.
ONSITE_HOST = 'localhost'
ONSITE_PORT = 9630
ONSITE_USERNAME = 'lightspeed'
ONSITE_PASSWORD = 'admin'
APP_ID = 'com.lightspeed.onsite.demo'
APP_VERSION = '1.0'
APP_PRIVATE_ID = '12345678-90ab-cdef-1234-567890abcdef'

product_id = 67
photo_id = 6
product_photo_url = 'https://%s:%d/api/products/%d/product_photos/%d/image/' % (
    ONSITE_HOST, ONSITE_PORT, product_id, photo_id)
logout_url = 'https://%s:%d/api/sessions/current/logout/' % (ONSITE_HOST, ONSITE_PORT)

# Setup a session for the http request. Note the accept header.
session = requests.Session()
session.auth = (ONSITE_USERNAME, ONSITE_PASSWORD)
session.headers.update({
    'user-agent': '%s/%s' % (APP_ID, APP_VERSION),
    'x-pappid': APP_PRIVATE_ID})
session.verify = False
session.stream = True

# Send the request to get a product photo.
with session.get(product_photo_url, headers={'accept': 'image/*;size=original'}) as response:
    
    # Assert that the request was successful.
    assert response.status_code == 200
    
    # Save the photo to disk.
    with open('output-file.jpg', 'wb') as photo:
        for raw_data in response.iter_content(chunk_size=1024):
            photo.write(raw_data)

    print('Downloaded output-file.jpg')

# Log out.
logout_response = session.post(logout_url)
assert logout_response.status_code == 204

The downloaded image:

Downloaded image

Searching Products

A POST request to /api/products/search/ is used to search products. The POST request payload specifies what invoice information to return (columns) and what to search by (filters). Pagination is supported with the search query.

A GET request to /api/products/search/ is used to obtain the list of columns and filters.

Listing Search Columns and Filters

A GET request to /api/products/search/ is used to obtain the list of columns and filters.

These results can be cached if desired.

"""
Get the list of search filters and columns for products using the OnSite API.
"""
import requests
from xml.dom import minidom

# Customize these to your install.
ONSITE_HOST = 'localhost'
ONSITE_PORT = 9630
ONSITE_USERNAME = 'lightspeed'
ONSITE_PASSWORD = 'admin'
APP_ID = 'com.lightspeed.onsite.demo'
APP_VERSION = '1.0'
APP_PRIVATE_ID = '12345678-90ab-cdef-1234-567890abcdef'

customer_search_url = 'https://%s:%d/api/products/search/' % (ONSITE_HOST, ONSITE_PORT)
logout_url = 'https://%s:%d/api/sessions/current/logout/' % (ONSITE_HOST, ONSITE_PORT)

# Create a session. This will persist cookies across all requests.
session = requests.Session()
session.auth = (ONSITE_USERNAME, ONSITE_PASSWORD)
session.headers.update({
    'user-agent': '%s/%s' % (APP_ID, APP_VERSION),
    'x-pappid': APP_PRIVATE_ID})
session.verify = False

# Send the request to get the list of columns/filters.
get_response = session.get(customer_search_url)
assert get_response.status_code == 200

# Log out.
logout_response = session.post(logout_url)
assert logout_response.status_code == 204

# Print the list of columns/filters.
response_xml = minidom.parseString(get_response.text)
print(response_xml.toprettyxml())
<!-- Response -->

<?xml version="1.0" ?>
<search_criteria uri="https://localhost:9630/api/products/search/">
	<columns>
		<column id="lsserver.search.column.code">
			<name>
				<localizable_message type="lsserver.search.column.code">
					<fields/>
					<plain_message>Code</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
			<required>true</required>
			<default>true</default>
			<sort_default>true</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.description">
			<name>
				<localizable_message type="lsserver.search.column.description">
					<fields/>
					<plain_message>Description</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
			<required>false</required>
			<default>true</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.sell">
			<name>
				<localizable_message type="lsserver.search.column.sell">
					<fields/>
					<plain_message>Sell</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
			<required>false</required>
			<default>true</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.inventory">
			<name>
				<localizable_message type="lsserver.search.column.inventory">
					<fields/>
					<plain_message>Inventory</plain_message>
				</localizable_message>
			</name>
			<type>DECIMAL</type>
			<required>false</required>
			<default>true</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.matrix_master">
			<name>
				<localizable_message type="lsserver.search.column.matrix_master">
					<fields/>
					<plain_message>Matrix Master</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
			<required>false</required>
			<default>true</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.inventoried">
			<name>
				<localizable_message type="lsserver.search.column.inventoried">
					<fields/>
					<plain_message>Inventoried</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
			<required>false</required>
			<default>true</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.quantity_total">
			<name>
				<localizable_message type="lsserver.search.column.quantity_total">
					<fields/>
					<plain_message>Quantity Total</plain_message>
				</localizable_message>
			</name>
			<type>DECIMAL</type>
			<required>false</required>
			<default>true</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.quantity_reserved">
			<name>
				<localizable_message type="lsserver.search.column.quantity_reserved">
					<fields/>
					<plain_message>Quantity Reserved</plain_message>
				</localizable_message>
			</name>
			<type>DECIMAL</type>
			<required>false</required>
			<default>true</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.quantity_in_warehouses">
			<name>
				<localizable_message type="lsserver.search.column.quantity_in_warehouses">
					<fields/>
					<plain_message>Quantity in Warehouses</plain_message>
				</localizable_message>
			</name>
			<type>DECIMAL</type>
			<required>false</required>
			<default>true</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.quantity_ordered_for_customers">
			<name>
				<localizable_message type="lsserver.search.column.quantity_ordered_for_customers">
					<fields/>
					<plain_message>Quantity Ordered For Customers</plain_message>
				</localizable_message>
			</name>
			<type>DECIMAL</type>
			<required>false</required>
			<default>true</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.quantity_ordered_for_stock">
			<name>
				<localizable_message type="lsserver.search.column.quantity_ordered_for_stock">
					<fields/>
					<plain_message>Quantity Ordered For Stock</plain_message>
				</localizable_message>
			</name>
			<type>DECIMAL</type>
			<required>false</required>
			<default>true</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.id">
			<name>
				<localizable_message type="lsserver.search.column.id">
					<fields/>
					<plain_message>ID</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
			<required>false</required>
			<default>false</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.size">
			<name>
				<localizable_message type="lsserver.search.column.size">
					<fields/>
					<plain_message>Size</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
			<required>false</required>
			<default>false</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.color">
			<name>
				<localizable_message type="lsserver.search.column.color">
					<fields/>
					<plain_message>Color</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
			<required>false</required>
			<default>false</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.family">
			<name>
				<localizable_message type="lsserver.search.column.family">
					<fields/>
					<plain_message>Family</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
			<required>false</required>
			<default>false</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.class">
			<name>
				<localizable_message type="lsserver.search.column.class">
					<fields/>
					<plain_message>Class</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
			<required>false</required>
			<default>false</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.web">
			<name>
				<localizable_message type="lsserver.search.column.web">
					<fields/>
					<plain_message>Web</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
			<required>false</required>
			<default>false</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.upc">
			<name>
				<localizable_message type="lsserver.search.column.upc">
					<fields/>
					<plain_message>UPC</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
			<required>false</required>
			<default>false</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.on_sale">
			<name>
				<localizable_message type="lsserver.search.column.on_sale">
					<fields/>
					<plain_message>On Sale</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
			<required>false</required>
			<default>false</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
		<column id="lsserver.search.column.inventory_version">
			<name>
				<localizable_message type="lsserver.search.column.inventory_version">
					<fields/>
					<plain_message>Inventory Version</plain_message>
				</localizable_message>
			</name>
			<type>INTEGER</type>
			<required>false</required>
			<default>false</default>
			<sort_default>false</sort_default>
			<sort_default_order_by>ASC</sort_default_order_by>
			<can_summarize>false</can_summarize>
		</column>
	</columns>
	<filters>
		<filter id="lsserver.search.filters.product">
			<name>
				<localizable_message type="lsserver.search.filters.product">
					<fields/>
					<plain_message>Product</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.code">
			<name>
				<localizable_message type="lsserver.search.filters.code">
					<fields/>
					<plain_message>Code</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.supplier_code">
			<name>
				<localizable_message type="lsserver.search.filters.supplier_code">
					<fields/>
					<plain_message>Supplier Code</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.description">
			<name>
				<localizable_message type="lsserver.search.filters.description">
					<fields/>
					<plain_message>Description</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.upc">
			<name>
				<localizable_message type="lsserver.search.filters.upc">
					<fields/>
					<plain_message>UPC</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.selling_price">
			<name>
				<localizable_message type="lsserver.search.filters.selling_price">
					<fields/>
					<plain_message>Selling Price</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.quantity">
			<name>
				<localizable_message type="lsserver.search.filters.quantity">
					<fields/>
					<plain_message>Quantity</plain_message>
				</localizable_message>
			</name>
			<type>DECIMAL</type>
		</filter>
		<filter id="lsserver.search.filters.matrix_master_product">
			<name>
				<localizable_message type="lsserver.search.filters.matrix_master_product">
					<fields/>
					<plain_message>Matrix Master Product</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
		</filter>
		<filter id="lsserver.search.filters.inventoried">
			<name>
				<localizable_message type="lsserver.search.filters.inventoried">
					<fields/>
					<plain_message>Inventoried</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
		</filter>
		<filter id="lsserver.search.filters.size">
			<name>
				<localizable_message type="lsserver.search.filters.size">
					<fields/>
					<plain_message>Size</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.color">
			<name>
				<localizable_message type="lsserver.search.filters.color">
					<fields/>
					<plain_message>Color</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.family">
			<name>
				<localizable_message type="lsserver.search.filters.family">
					<fields/>
					<plain_message>Family</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.class">
			<name>
				<localizable_message type="lsserver.search.filters.class">
					<fields/>
					<plain_message>Class</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.notes">
			<name>
				<localizable_message type="lsserver.search.filters.notes">
					<fields/>
					<plain_message>Notes</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.supplier">
			<name>
				<localizable_message type="lsserver.search.filters.supplier">
					<fields/>
					<plain_message>Supplier</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.default_raw_cost">
			<name>
				<localizable_message type="lsserver.search.filters.default_raw_cost">
					<fields/>
					<plain_message>Default Raw Cost</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.default_currency">
			<name>
				<localizable_message type="lsserver.search.filters.default_currency">
					<fields/>
					<plain_message>Default Currency</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.default_cost">
			<name>
				<localizable_message type="lsserver.search.filters.default_cost">
					<fields/>
					<plain_message>Default Cost</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.tax_status">
			<name>
				<localizable_message type="lsserver.search.filters.tax_status">
					<fields/>
					<plain_message>Tax Status</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.margin">
			<name>
				<localizable_message type="lsserver.search.filters.margin">
					<fields/>
					<plain_message>Margin</plain_message>
				</localizable_message>
			</name>
			<type>DECIMAL</type>
		</filter>
		<filter id="lsserver.search.filters.reorder_amount">
			<name>
				<localizable_message type="lsserver.search.filters.reorder_amount">
					<fields/>
					<plain_message>Reorder Amount</plain_message>
				</localizable_message>
			</name>
			<type>DECIMAL</type>
		</filter>
		<filter id="lsserver.search.filters.reorder_point">
			<name>
				<localizable_message type="lsserver.search.filters.reorder_point">
					<fields/>
					<plain_message>Reorder Point</plain_message>
				</localizable_message>
			</name>
			<type>DECIMAL</type>
		</filter>
		<filter id="lsserver.search.filters.editable_description">
			<name>
				<localizable_message type="lsserver.search.filters.editable_description">
					<fields/>
					<plain_message>Editable Description</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
		</filter>
		<filter id="lsserver.search.filters.editable_sell">
			<name>
				<localizable_message type="lsserver.search.filters.editable_sell">
					<fields/>
					<plain_message>Editable Sell</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
		</filter>
		<filter id="lsserver.search.filters.serialized">
			<name>
				<localizable_message type="lsserver.search.filters.serialized">
					<fields/>
					<plain_message>Serialized</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
		</filter>
		<filter id="lsserver.search.filters.sell_price_level_a">
			<name>
				<localizable_message type="lsserver.search.filters.sell_price_level_a">
					<fields/>
					<plain_message>Sell Price Level A</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.sell_price_level_b">
			<name>
				<localizable_message type="lsserver.search.filters.sell_price_level_b">
					<fields/>
					<plain_message>Sell Price Level B</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.sell_price_level_c">
			<name>
				<localizable_message type="lsserver.search.filters.sell_price_level_c">
					<fields/>
					<plain_message>Sell Price Level C</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.sell_price_level_d">
			<name>
				<localizable_message type="lsserver.search.filters.sell_price_level_d">
					<fields/>
					<plain_message>Sell Price Level D</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.sell_price_level_e">
			<name>
				<localizable_message type="lsserver.search.filters.sell_price_level_e">
					<fields/>
					<plain_message>Sell Price Level E</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.sell_price_level_f">
			<name>
				<localizable_message type="lsserver.search.filters.sell_price_level_f">
					<fields/>
					<plain_message>Sell Price Level F</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.sell_price_level_g">
			<name>
				<localizable_message type="lsserver.search.filters.sell_price_level_g">
					<fields/>
					<plain_message>Sell Price Level G</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.sell_price_level_h">
			<name>
				<localizable_message type="lsserver.search.filters.sell_price_level_h">
					<fields/>
					<plain_message>Sell Price Level H</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.sell_price_level_i">
			<name>
				<localizable_message type="lsserver.search.filters.sell_price_level_i">
					<fields/>
					<plain_message>Sell Price Level I</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.sell_price_level_j">
			<name>
				<localizable_message type="lsserver.search.filters.sell_price_level_j">
					<fields/>
					<plain_message>Sell Price Level J</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.cost_price_level_a">
			<name>
				<localizable_message type="lsserver.search.filters.cost_price_level_a">
					<fields/>
					<plain_message>Cost Price Level A</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.cost_price_level_b">
			<name>
				<localizable_message type="lsserver.search.filters.cost_price_level_b">
					<fields/>
					<plain_message>Cost Price Level B</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.cost_price_level_c">
			<name>
				<localizable_message type="lsserver.search.filters.cost_price_level_c">
					<fields/>
					<plain_message>Cost Price Level C</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.cost_price_level_d">
			<name>
				<localizable_message type="lsserver.search.filters.cost_price_level_d">
					<fields/>
					<plain_message>Cost Price Level D</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.cost_price_level_e">
			<name>
				<localizable_message type="lsserver.search.filters.cost_price_level_e">
					<fields/>
					<plain_message>Cost Price Level E</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.cost_price_level_f">
			<name>
				<localizable_message type="lsserver.search.filters.cost_price_level_f">
					<fields/>
					<plain_message>Cost Price Level F</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.cost_price_level_g">
			<name>
				<localizable_message type="lsserver.search.filters.cost_price_level_g">
					<fields/>
					<plain_message>Cost Price Level G</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.cost_price_level_h">
			<name>
				<localizable_message type="lsserver.search.filters.cost_price_level_h">
					<fields/>
					<plain_message>Cost Price Level H</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.cost_price_level_i">
			<name>
				<localizable_message type="lsserver.search.filters.cost_price_level_i">
					<fields/>
					<plain_message>Cost Price Level I</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.cost_price_level_j">
			<name>
				<localizable_message type="lsserver.search.filters.cost_price_level_j">
					<fields/>
					<plain_message>Cost Price Level J</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.gl_asset_account">
			<name>
				<localizable_message type="lsserver.search.filters.gl_asset_account">
					<fields/>
					<plain_message>GL Asset Account</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.gl_cogs_expense_account">
			<name>
				<localizable_message type="lsserver.search.filters.gl_cogs_expense_account">
					<fields/>
					<plain_message>GL COGS Expense Account</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.gl_income_account">
			<name>
				<localizable_message type="lsserver.search.filters.gl_income_account">
					<fields/>
					<plain_message>GL Income Account</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.gl_payable_expense_account">
			<name>
				<localizable_message type="lsserver.search.filters.gl_payable_expense_account">
					<fields/>
					<plain_message>GL Payable Expense Account</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.web">
			<name>
				<localizable_message type="lsserver.search.filters.web">
					<fields/>
					<plain_message>Web</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
		</filter>
		<filter id="lsserver.search.filters.web_description">
			<name>
				<localizable_message type="lsserver.search.filters.web_description">
					<fields/>
					<plain_message>Web Description</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.web_keyword">
			<name>
				<localizable_message type="lsserver.search.filters.web_keyword">
					<fields/>
					<plain_message>Web Keyword</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.web_price">
			<name>
				<localizable_message type="lsserver.search.filters.web_price">
					<fields/>
					<plain_message>Web Price</plain_message>
				</localizable_message>
			</name>
			<type>MONEY</type>
		</filter>
		<filter id="lsserver.search.filters.product_id">
			<name>
				<localizable_message type="lsserver.search.filters.product_id">
					<fields/>
					<plain_message>Product ID</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.multi_store_label">
			<name>
				<localizable_message type="lsserver.search.filters.multi_store_label">
					<fields/>
					<plain_message>Multi-Store Label</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.current">
			<name>
				<localizable_message type="lsserver.search.filters.current">
					<fields/>
					<plain_message>Current</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
		</filter>
		<filter id="lsserver.search.filters.matrix_child_product">
			<name>
				<localizable_message type="lsserver.search.filters.matrix_child_product">
					<fields/>
					<plain_message>Matrix Child Product</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
		</filter>
		<filter id="lsserver.search.filters.new_import">
			<name>
				<localizable_message type="lsserver.search.filters.new_import">
					<fields/>
					<plain_message>New Import</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
		</filter>
		<filter id="lsserver.search.filters.new_updates">
			<name>
				<localizable_message type="lsserver.search.filters.new_updates">
					<fields/>
					<plain_message>New Updates</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
		</filter>
		<filter id="lsserver.search.filters.new_costs">
			<name>
				<localizable_message type="lsserver.search.filters.new_costs">
					<fields/>
					<plain_message>New Costs</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
		</filter>
		<filter id="lsserver.search.filters.created_date">
			<name>
				<localizable_message type="lsserver.search.filters.created_date">
					<fields/>
					<plain_message>Created Date</plain_message>
				</localizable_message>
			</name>
			<type>DATE</type>
		</filter>
		<filter id="lsserver.search.filters.modified_date">
			<name>
				<localizable_message type="lsserver.search.filters.modified_date">
					<fields/>
					<plain_message>Modified Date</plain_message>
				</localizable_message>
			</name>
			<type>DATE</type>
		</filter>
		<filter id="lsserver.search.filters.photo">
			<name>
				<localizable_message type="lsserver.search.filters.photo">
					<fields/>
					<plain_message>Photo</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
		</filter>
		<filter id="lsserver.search.filters.on_sale">
			<name>
				<localizable_message type="lsserver.search.filters.on_sale">
					<fields/>
					<plain_message>On Sale</plain_message>
				</localizable_message>
			</name>
			<type>BOOLEAN</type>
		</filter>
		<filter id="lsserver.search.filters.sale_name">
			<name>
				<localizable_message type="lsserver.search.filters.sale_name">
					<fields/>
					<plain_message>Sale Name</plain_message>
				</localizable_message>
			</name>
			<type>STRING</type>
		</filter>
		<filter id="lsserver.search.filters.inventory_version">
			<name>
				<localizable_message type="lsserver.search.filters.inventory_version">
					<fields/>
					<plain_message>Inventory Version</plain_message>
				</localizable_message>
			</name>
			<type>INTEGER</type>
		</filter>
	</filters>
</search_criteria>

Search by Date

A POST request to /api/products/search/ is used to search products.

Search by Description

"""
Search products using the OnSite API.
"""
import requests
from xml.dom import minidom

# Customize these to your install.
ONSITE_HOST = 'localhost'
ONSITE_PORT = 9630
ONSITE_USERNAME = 'lightspeed'
ONSITE_PASSWORD = 'admin'
APP_ID = 'com.lightspeed.onsite.demo'
APP_VERSION = '1.0'
APP_PRIVATE_ID = '12345678-90ab-cdef-1234-567890abcdef'

product_search_url = 'https://%s:%d/api/products/search/' % (ONSITE_HOST, ONSITE_PORT)
logout_url = 'https://%s:%d/api/sessions/current/logout/' % (ONSITE_HOST, ONSITE_PORT)

# This is the search information.
search_xml = """
<search>
    <page>
        <offset>0</offset>
        <count>5</count>
    </page>
    <search_query>
        <columns>
            <column id="lsserver.search.column.id"/>
            <column id="lsserver.search.column.code"/>
            <column id="lsserver.search.column.description"/>
            <column id="lsserver.search.column.sell"/>
        </columns>
        <sort_by_column>
            <column id="lsserver.search.column.code"/>
            <order_by>ASC</order_by>
        </sort_by_column>
        <filters>lsserver.search.filters.description CONTAINS "Bikini"</filters>
    </search_query>
</search>
"""

# Create a session. This will persist cookies across all requests.
session = requests.Session()
session.auth = (ONSITE_USERNAME, ONSITE_PASSWORD)
session.headers.update({
    'user-agent': '%s/%s' % (APP_ID, APP_VERSION),
    'x-pappid': APP_PRIVATE_ID})
session.verify = False

# Send the request to search the products.
post_response = session.post(product_search_url, data=search_xml)
assert post_response.status_code == 200

# Log out.
logout_response = session.post(logout_url)
assert logout_response.status_code == 204

# Print the search results.
response_xml = minidom.parseString(post_response.text)
print(response_xml.toprettyxml())
<!-- Response -->

<?xml version="1.0" ?>
<data>
   <info>
       <total_count>1</total_count>
   </info>
   <columns>
       <column>
           <name>
               <localizable_message type="lsserver.search.column.id">
                   <fields/>
                   <plain_message>ID</plain_message>
               </localizable_message>
           </name>
           <type>STRING</type>
       </column>
       <column>
           <name>
               <localizable_message type="lsserver.search.column.code">
                   <fields/>
                   <plain_message>Code</plain_message>
               </localizable_message>
           </name>
           <type>STRING</type>
       </column>
       <column>
           <name>
               <localizable_message type="lsserver.search.column.description">
                   <fields/>
                   <plain_message>Description</plain_message>
               </localizable_message>
           </name>
           <type>STRING</type>
       </column>
       <column>
           <name>
               <localizable_message type="lsserver.search.column.sell">
                   <fields/>
                   <plain_message>Sell</plain_message>
               </localizable_message>
           </name>
           <type>MONEY</type>
       </column>
   </columns>
   <rows>
       <row>
           <links>
               <link>
                   <product id="7" uri="https://localhost:9630/api/products/7/"/>
               </link>
           </links>
           <cell>
               <type>STRING</type>
               <value>P-1006</value>
           </cell>
           <cell>
               <type>STRING</type>
               <value>333-003-02:35PM</value>
           </cell>
           <cell>
               <type>STRING</type>
               <value>Lucy's Bikini-02:35PM</value>
           </cell>
           <cell>
               <type>MONEY</type>
               <value>10.000</value>
           </cell>
       </row>
   </rows>
</data>