Python API for accessing the REST API of the Xero accounting tool.

License: BSD-3-Clause

Language: Python


Build Status

PyXero is a Python API for accessing the REST API provided by the Xero accounting tool. It allows access to both Public, Private and Partner applications.


Install this library using the python package manager:

pip install pyxero

You'll need to follow the Xero Developer documentation to register your application. Do that as follows:

Public Applications

Public applications use a 3-step OAuth process.

When you register your public application with Xero, you'll be given a Consumer Key and a Consumer secret. These are both strings.

To access the Xero API you must first create some credentials:

>>> from xero.auth import PublicCredentials
>>> credentials = PublicCredentials(<consumer_key>, <consumer_secret>)
>>> print credentials.url

You now direct the user to visit the URL described by credentials.url. They will be asked to log into their Xero account, and then shown a request to authenticate your request to access the user's account. When the allow access, they will be directed to a page that gives them a 6-digit verification number. Put this verifier number into a string, and call verify() on the credentials object::

>>> credentials.verify(<verifier string>)

This will verify your credentials, and retrieve an access token. You can then use your credentials to instantiate an instance of the Xero API::

>>> from xero import Xero
>>> xero = Xero(credentials)

Public Applications with verification by callback

Public applications can also be validated using a callback URI. If this approach is used, the user won't be given a verification number. Instead, when they authorize the OAuth request, their browser will be redirected to a pre-configured callback URI, which will deliver the validation token directly to your application.

To use a callback, you must provide a domain as part of your Xero application registration; then, you provide a URL under that domain as the third argument when creating the credentials::

>>> credentials = PublicCredentials(<consumer_key>, <consumer_secret>, <callback_uri>)
>>> print credentials.url

When the user authorizes access to their Xero account, the callback_url will be called with three GET arguments:

  • oauth_token: The oauth_token that this request belongs to
  • oauth_verifier: The verifier string
  • org: An identifier for the organization that is allowing access.

The verifier can then be used to verify the credentials, as with the manual process.

Reconstructing Public credentials

Public Applications use a 3-step OAuth process, and if you're doing this in a web application, you will usually lose the credentials object over the verification step. This means you need to be able to restore the credentials object when verification has been provided.

The state attribute of a credentials object contains all the details needed to reconstruct an instance of the credentials::

>>> saved_state = credentials.state
>>> print saved_state
{'consumer_key': '...', 'consumer_secret': '...', ...}

>>> new_credentials = PublicCredentials(**saved_state)

Private Applications

If using a Private application, you will need to install PyCrypto, a pure Python cryptographic module. You'll also need to generate an signed RSA certificate, and submit that certificate as part of registering your application with Xero. See the Xero Developer documentation for more details.

When you register your private application with Xero, you'll be given a Consumer Key. You'll also be given a Consumer secret - this can be ignored.

Using the Private credentials is much simpler than the Public credentials, because there's no verification step -- verification is managed using RSA signed API requests::

>>> from xero import Xero
>>> from xero.auth import PrivateCredentials
>>> with open(<path to rsa key file>) as keyfile:
...     rsa_key =
>>> credentials = PrivateCredentials(<consumer_key>, rsa_key)
>>> xero = Xero(credentials)

Follow these steps to generate a public/private key pair to sign your requests. You'll upload your public key when you create your Xero Private app at You'll use the private key (aka RSA key) to generate your oAuth signature.

The RSA key is a multi-line string that will look something like::


You can get this string by either reading the contents of your private key file into a variable, or storing the key value as a constant. If you choose to store the key value as a constant, remember two things:

  • DO NOT UNDER ANY CIRCUMSTANCES check this file into a public repository. It is your identity, and anyone with access to this file could masquerade as you.

  • Make sure there is no leading space before the -----BEGIN PRIVATE KEY----- portion of the string.

Partner Applications

Partner Application authentication works similarly to the 3-step OAuth used by Public Applications, but with RSA signed requests. Partner OAuth tokens still have a 30 minute expiry, but can be swapped for a new token at any time.

When you register your partner application with Xero, you'll have a Consumer Key, Consumer Secret and RSA Key. All three elements are required.

>>> from xero import Xero
>>> from xero.auth import PartnerCredentials
>>> credentials = PartnerCredentials(<consumer_key>, <consumer_secret>,
...                                  <rsa_key>)
>>> xero = Xero(credentials)

When using the API over an extended period, you will need to exchange tokens when they expire.

>>> if credentials.expired():
...     credentials.refresh()

Important: credentials.state changes after a token swap. Be sure to persist the new state.

Using the Xero API

This API is a work in progress. At present, there is no wrapper layer to help create real objects, it just returns dictionaries in the exact format provided by the Xero API. This will change into a more useful API before 1.0

The Xero API object exposes a simple API for retrieving and updating objects. For example, to deal with contacts::

# Retrieve all contact objects
>>> xero.contacts.all()
[{ info...}, { info...}, { info...}, ...]

# Retrieve a specific contact object
>>> xero.contacts.get(u'b2b5333a-2546-4975-891f-d71a8a640d23')
{ info...}

# Retrieve all contacts updated since 1 Jan 2013
>>> xero.contacts.filter(since=datetime(2013, 1, 1))
[{ info...}, { info...}, { info...}]

# Retrieve all contacts whose name is 'John Smith'
>>> xero.contacts.filter(Name='John Smith')
[{ info...}, { info...}, { info...}]

# Retrieve all contacts whose name starts with 'John'
>>> xero.contacts.filter(Name__startswith='John')
[{ info...}, { info...}, { info...}]

# Retrieve all contacts whose name ends with 'Smith'
>>> xero.contacts.filter(Name__endswith='Smith')
[{ info...}, { info...}, { info...}]

# Retrieve all contacts whose name starts with 'John' and ends with 'Smith'
>>> xero.contacts.filter(Name__startswith='John', Name__endswith='Smith')
[{ info...}, { info...}, { info...}]

# Retrieve all contacts whose name contains 'mit'
>>> xero.contacts.filter(Name__contains='mit')
[{ info...}, { info...}, { info...}]

# Create a new object
>>> xero.contacts.put({ info...})

# Create multiple new objects
>>> xero.contacts.put([{ info...}, { info...}, { info...}])

# Save an update to an existing object
>>> c = xero.contacts.get(u'b2b5333a-2546-4975-891f-d71a8a640d23')
>>> c['Name'] = 'John Smith'

# Save multiple objects
>>>[c1, c2])

Complex filters can be constructed in the Django-way, for example retrieving invoices for a contact:

>>> xero.invoices.filter(Contact_ContactID='83ad77d8-48a7-4f77-9146-e6933b7fb63b')

Filters which aren't supported by this API can also be constructed using 'raw' mode like this:

>>> xero.invoices.filter(raw='AmountDue > 0')

Be careful when dealing with large amounts of data, the Xero API will take an increasingly long time to respond, or an error will be returned. If a query might return more than 100 results, you should make use of the page parameter::

# Grab 100 invoices created after 01-01-2013
>>> xero.invoices.filter(since=datetime(2013, 1, 1), page=1)

You can also order the results to be returned::

# Grab contacts ordered by EmailAddress
>>> xero.contacts.filter(order='EmailAddress DESC')

For invoices (and other objects that can be retrieved as PDFs), accessing the PDF is done via setting the Accept header:

# Fetch a PDF
invoice = xero.invoices.get('af722e93-b64f-482d-9955-1b027bfec896', \
    headers={'Accept': 'application/pdf'})
# Stream the PDF to the user (Django specific example)
response = HttpResponse(invoice, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="invoice.pdf"'
return response

Download and uploading attachments is supported using the Xero GUID of the relevant object::

# List attachments on a contact
>>> xero.contacts.get_attachments(c['ContactID'])
[{...attachment info...}, {...attachment info...}]

# Attach a PDF to a contact
>>> f = open('form.pdf', 'rb')
>>> xero.contacts.put_attachment(c['ContactID'], 'form.pdf', f, 'application/pdf')
>>> f.close()

>>> xero.contacts.put_attachment_data(c['ContactID'], 'form.pdf', data, 'application/pdf')

# Download an attachment
>>> f = open('form.pdf', 'wb')
>>> xero.contacts.get_attachment(c['ContactID'], 'form.pdf', f)
>>> f.close()

>>> data = xero.contacts.get_attachment_data(c['ContactID'], 'form.pdf')

This same API pattern exists for the following API objects:

  • Accounts
  • Attachments
  • BankTransactions
  • BankTransfers
  • BrandingThemes
  • ContactGroups
  • Contacts
  • CreditNotes
  • Currencies
  • Employees
  • ExpenseClaims
  • Invoices
  • Items
  • Journals
  • ManualJournals
  • Organisation
  • Overpayments
  • Payments
  • Prepayments
  • Purchase Orders
  • Receipts
  • RepeatingInvoices
  • Reports
  • TaxRates
  • TrackingCategories
  • Users


In order to access the payroll methods from Xero, you can do it like this:


Within the payrollAPI you have access to:

  • employees
  • leaveapplications
  • payitems
  • payrollcalendars
  • payruns
  • payslip
  • timesheets

Under the hood

Using a wrapper around Xero API is a really nice feature, but it's also interesting to understand what is exactly happening under the hood.

Filter operator

filter operator wraps the "where" keyword in Xero API.

# Retrieves all contacts whose name is "John"
>>> xero.contacts.filter(name="John")

# Triggers this GET request:
Html encoded: <XERO_API_URL>/Contacts?where=name%3D%3D%22John%22
Non encoded:  <XERO_API_URL>/Contacts?where=name=="John"

Several parameters are separated with encoded '&&' characters:

# Retrieves all contacts whose first name is "John" and last name is "Doe"
>>> xero.contacts.filter(firstname="John", lastname="Doe")

# Triggers this GET request:
Html encoded: <XERO_API_URL>/Contacts?where=lastname%3D%3D%22Doe%22%26%26firstname%3D%3D%22John%22
Non encoded:  <XERO_API_URL>/Contacts?where=lastname=="Doe"&&firstname=="John"

Underscores are automatically converted as "dots":

# Retrieves all contacts whose name is "John"
>>> xero.contacts.filter(first_name="John")

# Triggers this GET request:
Html encoded: <XERO_API_URL>/Contacts?
Non encoded:  <XERO_API_URL>/Contacts?"John"


If you're going to run the PyXero test suite, in addition to the dependencies for PyXero, you need to add the following dependency to your environment:

mock >= 1.0

Mock isn't included in the formal dependencies because they aren't required for normal operation of PyXero. It's only required for testing purposes.

Once you've installed these dependencies, you can run the test suite by running the following from the root directory of the project:

$ python test

If you find any problems with PyXero, you can log them on Github Issues. When reporting problems, it's extremely helpful if you can provide reproduction instructions -- the sequence of calls and/or test data that can be used to reproduce the issue.

New features or bug fixes can be submitted via a pull request. If you want your pull request to be merged quickly, make sure you either include regression test(s) for the behavior you are adding/fixing, or provide a good explanation of why a regression test isn't possible.

Project Statistics

Sourcerank 8
Repository Size 271 KB
Stars 203
Forks 173
Watchers 34
Open issues 63
Dependencies 6
Contributors 39
Tags 14
Last updated
Last pushed

Top Contributors See all

Aidan Lister Russell Keith-Magee Romain Garrigues MJMortimer MatthewMXero Robert Wallhead Bertil Matthew Schinckel Jarek Glowacki Brendan Jurd Albert Wang Matt Healy gavinhodge timmygee João Miguel Neves Sidney Maestre Nick Farrell Jan Kroeze Ari Maniatis jacobg

Packages Referencing this Repo

Python API for accessing the REST API of the Xero accounting tool.
Latest release 0.0.2 - Updated - 203 stars
Python API for accessing the REST API of the Xero accounting tool.
Latest release 0.9.1 - Updated - 203 stars

Recent Tags See all

v0.9.1 August 27, 2018
v0.9.0 May 11, 2017
v0.8.0 March 21, 2016
v0.8.0-beta3 October 13, 2015
v0.8.0-beta2 October 13, 2015
v0.8.0-beta1 October 11, 2015
v0.7.0 July 01, 2015
v0.7.0-alpha2 June 10, 2015
v0.7.0-alpha1 June 09, 2015
v0.6.0 January 29, 2015
v0.6.0-rc.1 August 25, 2014
v0.5.2 July 29, 2013
v0.5.1 May 31, 2013
v0.5 March 28, 2013

Interesting Forks See all

Python API for accessing the REST API of the Xero accounting tool.
Python - BSD-3-Clause - Last pushed - 1 stars - 3 forks
Python API for accessing the REST API of the Xero accounting tool.
Python - Updated - 1 stars

Something wrong with this page? Make a suggestion

Last synced: 2019-04-18 06:49:50 UTC

Login to resync this repository