Install & Compatibility
Where this runs
tested against v14.0.0 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.910 runs
installs and imports cleanly · install 0.0s · import 0.362s · 32MB
glibcpy 3.10–3.910 runs
installs and imports cleanly · install 4.3s · import 0.321s · 33MB
34MB installed
● package 34MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
ApiClient
✓ from xero_python.api_client import ApiClient
Main client for API configuration and token management.
Configuration
✓ from xero_python.api_client import Configuration
Used to configure the API client, e.g., base URL.
AccountingApi
✓ from xero_python.accounting import AccountingApi
✗ from xero_python import AccountingApi
API clients are now located in sub-modules corresponding to Xero API domains. This changed in v10.0.0.
IdentityApi
✓ from xero_python.identity import IdentityApi
Used for managing connections and retrieving tenant IDs.
Contact
✓ from xero_python.models.accounting import Contact
Models for specific API domains are in `xero_python.models.<domain>`.
This example demonstrates how to initialize the Xero Python SDK and make a simple API call (getting contacts), assuming you have already obtained an OAuth2 refresh token, access token, client ID, client secret, and a specific Xero organisation's tenant ID. In a real application, the OAuth2 authorization flow would be executed to obtain these tokens and they should be stored securely and refreshed automatically.
import os
import json
from xero_python.accounting import AccountingApi
from xero_python.identity import IdentityApi
from xero_python.api_client import ApiClient, Configuration, ApiException
# IMPORTANT: In a real application, you would implement the full OAuth2 flow
# to obtain and refresh tokens securely. This example assumes you have
# a valid access token, refresh token, and tenant ID.
# NEVER hardcode secrets or tokens in production code.
# Load your Xero API credentials from environment variables
CLIENT_ID = os.environ.get("XERO_CLIENT_ID", "YOUR_CLIENT_ID")
CLIENT_SECRET = os.environ.get("XERO_CLIENT_SECRET", "YOUR_CLIENT_SECRET")
REFRESH_TOKEN = os.environ.get("XERO_REFRESH_TOKEN", "YOUR_REFRESH_TOKEN")
ACCESS_TOKEN = os.environ.get("XERO_ACCESS_TOKEN", "YOUR_ACCESS_TOKEN") # Often short-lived
TENANT_ID = os.environ.get("XERO_TENANT_ID", "YOUR_TENANT_ID") # Specific Xero organisation ID
# Initialize Xero API client configuration
config = Configuration()
# config.host can be set for specific regions if needed, e.g., 'https://api.xero.com' (default)
api_client = ApiClient(config)
# Set the OAuth2 configuration for token management (essential for refresh)
api_client.set_oauth2_config(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
tenant_id=TENANT_ID # This is the Xero Organisation ID you want to interact with
)
# Manually set an initial token set (for demonstration purposes)
# In a real app, you'd load this from secure storage and refresh as needed.
token_set = {
"access_token": ACCESS_TOKEN,
"refresh_token": REFRESH_TOKEN,
"expires_at": 0 # Set to 0 to force immediate refresh if ACCESS_TOKEN is dummy/expired
}
api_client.set_token_set(json.dumps(token_set))
# --- Token Refresh (Crucial for long-lived applications) ---
# The SDK automatically tries to refresh if token_set.expires_at is in the past
# when making an API call. For explicit control, you might call:
try:
api_client.refresh_token_set(token_set["refresh_token"])
# After refresh, the token_set in api_client is updated.
# You should persist this new token_set securely.
# print("Token refreshed successfully.")
except ApiException as e:
print(f"Error refreshing token: {e.body}")
exit() # Cannot proceed without a valid token
# Initialize the Accounting API client
accounting_api = AccountingApi(api_client)
try:
# Example: Get the first 5 contacts
contacts = accounting_api.get_contacts(_page=1, _limit=5)
if contacts.contacts:
print("Successfully retrieved contacts:")
for contact in contacts.contacts:
print(f"- {contact.name} (ID: {contact.contact_id})")
else:
print("No contacts found.")
except ApiException as e:
print(f"Xero API Error: Status {e.status}, Body: {e.body}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
Debug
Known issues
breakingVersion 10.0.0 introduced major breaking changes, including the complete removal of OAuth1 support and a full rewrite of the SDK's internal structure.fixMigrate your authentication flow to OAuth2 and update all API client and model imports. Consult the official migration guide for detailed steps.
affects: 10.0.0+
gotchaThe SDK's OAuth2 flow for web applications requires managing state and handling callbacks to securely obtain tokens. It's not a simple client_credentials grant.fixImplement the full OAuth2 Authorization Code Grant flow, including handling the redirect URL and state parameter, typically using a web framework like Flask (see official examples).
affects: All versions (OAuth2)
gotchaAccess tokens are short-lived. Long-lived applications must implement robust refresh token management, including persisting the new token set after each refresh.fixAfter obtaining a refresh token, store it securely. Use `api_client.refresh_token_set()` to get a new access token and update the entire token set in your persistent storage.
affects: All versions (OAuth2)
gotchaAll API calls require a `tenant_id` (representing a specific Xero organisation). This ID is not part of the initial token set and must be retrieved separately.fixAfter successful authentication, use `IdentityApi().get_connections()` to list the organisations the user has connected. Extract the desired `tenant_id` from this list and set it using `api_client.set_tenant_id(tenant_id)`.
affects: All versions (OAuth2)
Errors
Common errors & fixes
xero_python.exceptions.ApiException: (401) Reason: Unauthorized
The provided access token is invalid, expired, or the `tenant_id` is incorrect/missing for the requested scope.
fixEnsure your access token is valid and refreshed if necessary. Verify the `tenant_id` is correctly set on the `ApiClient` instance. Check the scopes requested during authentication match the API call.
AttributeError: module 'xero_python' has no attribute 'Xero'
Attempting to use an old SDK import pattern or class name from versions prior to 10.0.0.
fixUpdate your imports. The main API clients are now specific to domains, e.g., `from xero_python.accounting import AccountingApi`. The `Xero` class no longer exists.
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0) or TypeError: 'int' object is not subscriptable
The `token_set` passed to `api_client.set_token_set()` is not a valid JSON string, or it's not a dictionary when expected.
fixEnsure the token set is stored and loaded as a JSON string (e.g., from a file or database) and parsed into a Python dictionary before passing it to `set_token_set()`, or ensure `set_token_set` receives the JSON string directly (it expects a JSON string, not a dict).
xero_python.exceptions.ApiException: (400) Reason: Bad Request - {'error': 'unsupported_grant_type', 'error_description': 'The authorization grant type is not supported by the authorization server.'}
You are attempting an OAuth2 grant type that is not supported by Xero (e.g., trying client_credentials for private apps, which Xero doesn't directly support for most API calls with the SDK).
fixEnsure you are following the correct OAuth2 Authorization Code Grant flow. Xero typically requires user consent for most API access, not just client credentials. Refer to the official Xero OAuth2 documentation.
Upgrade
Version history
14.0.0latest on PyPI · released Jun 4, 2026
Audit
Dependencies
No dependency data recorded yet.