Skip to main content

XeroConnect

PyPI version Python versions License: MIT

A lightweight Python client for core Xero Accounting API workflows.

XeroConnect is not a full Xero SDK, ERP, or sync engine. It wraps a focused set of endpoints — OAuth, invoices, contacts, payments, and Profit & Loss reports — with a small API surface and a single runtime dependency (httpx).

Who This Package Is For

XeroConnect is a good fit if you:

  • Need a small, readable client for common accounting workflows in production systems
  • Want plain JSON dict responses without generated model classes
  • Already know which Xero endpoints you need and do not require full API coverage
  • Prefer a minimal dependency footprint (httpx only)
  • Are building scripts, internal tools, or backend services that list invoices, fetch contacts, read payments, or pull P&L reports

Who This Package Is Not For

XeroConnect is not the right choice if you:

  • Need broad Xero API coverage (payroll, projects, assets, files, bank feeds, and more)
  • Want official Xero-maintained SDKs with OpenAPI-generated models and published API reference docs
  • Are building a full Xero App Store application and need sample apps, helper methods, and platform-specific integration patterns
  • Require PKCE OAuth flows for desktop or mobile apps (not supported)
  • Need create endpoints for all resources (invoice create, contact create, and payment create are not included)

XeroConnect vs Other Python Libraries

Several Python libraries exist for Xero. Here is how they differ in scope and design.

xero-python pyxero xerosdk XeroConnect
Maintainer Xero Developer API (official) Community Third-party (Fyle) Independent open source
API coverage Full — Accounting, Payroll (AU/UK/NZ), Projects, Assets, Files Accounting endpoints via dict API Subset — Invoices, Contacts, Accounts, Items, Tracking Focused subset — OAuth, Invoices, Contacts, Payments, P&L
Response format Generated model classes Dicts Class-based wrappers Dicts
HTTP library urllib3 (via generated client) requests requests httpx
OAuth Built-in token management helpers OAuth2 with PKCE support Refresh token on connection Separate OAuthClient
Package size Large (OpenAPI-generated) Moderate Small Small

When to Use XeroConnect

Use XeroConnect when your integration only needs the endpoints it supports, you value a small install and a simple resource-based API (client.invoices.list()), and plain dict responses are sufficient for your application.

When to Use the Official SDK (xero-python)

Use the official Xero Python SDK when you need full API coverage across Accounting and other Xero API sets (Payroll, Projects, Assets, Files), typed model classes generated from Xero's OpenAPI specification, official documentation and sample applications, or long-term alignment with Xero's published SDK conventions.

When to Use Other Libraries

  • pyxero — established community library with dict-based access to accounting endpoints and PKCE OAuth support. A reasonable choice if you need broader accounting CRUD with a dict-oriented API.
  • xerosdk — small third-party SDK covering a limited set of resource classes with built-in token refresh on the connection object.

Positioning

XeroConnect is a focused Python client for production accounting workflows — OAuth, invoices, contacts, payments, and Profit & Loss reports — without the scope of a full API SDK.

What It Supports

Core Endpoints

Area Methods
OAuth Authorization URL, token exchange, refresh token, connections, tenant ID helper
Invoices List, get, update (with optional payload wrapping)
Contacts List, get
Payments List, get
Reports Profit and Loss
Webhooks Signature verification
Helpers Date filter builder, access token provider hook

Reports

Report Method
Profit & Loss client.reports.profit_and_loss()

Installation

Install from PyPI:

pip install xeroconnect

For development:

git clone https://github.com/alixaprodev/xeroconnect.git
cd xeroconnect
pip install -e ".[dev]"

OAuth Flow

Use OAuthClient to handle the authorization flow separately from API calls:

from xeroconnect import OAuthClient

oauth = OAuthClient(
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
    redirect_uri="https://yourapp.com/callback",
)

# Step 1: Redirect the user to this URL
auth_url = oauth.get_authorization_url(
    scopes=[
        "openid",
        "profile",
        "email",
        "accounting.transactions",
        "accounting.contacts",
        "offline_access",
    ],
    state="random-state-string",
)

# Step 2: Exchange the authorization code for tokens
tokens = oauth.exchange_code("authorization-code-from-callback")
access_token = tokens["access_token"]
refresh_token = tokens["refresh_token"]

# Step 3: Resolve tenant ID from connections
tenant_id = oauth.get_tenant_id(access_token)

# Or inspect all connected organisations
connections = oauth.get_connections(access_token)

# Step 4: Refresh when the access token expires
tokens = oauth.refresh_token(refresh_token)
# Xero rotates refresh tokens — persist the new refresh_token from the response

Token requests use HTTP Basic authentication (client_id:client_secret in the Authorization header) with a form-encoded body. This follows RFC 6749 and is supported by Xero alongside credential-in-body approaches.

Recommended OAuth scopes

For common accounting workflows (invoices, contacts, payments, reports):

scopes = [
    "openid",
    "profile",
    "email",
    "accounting.transactions",
    "accounting.transactions.read",
    "accounting.contacts",
    "accounting.contacts.read",
    "accounting.reports.read",
    "accounting.settings",
    "accounting.settings.read",
    "offline_access",
]

Basic Client Usage

from xeroconnect import XeroClient

client = XeroClient(
    access_token="your-access-token",
    tenant_id="your-tenant-id",
)

# Use as a context manager to ensure connections are closed
with XeroClient(access_token="...", tenant_id="...") as client:
    invoices = client.invoices.list()

Token refresh pattern

XeroConnect does not store tokens. Use an access_token_provider so the client can recover from expired access tokens:

from xeroconnect import OAuthClient, XeroClient

oauth = OAuthClient(client_id="...", client_secret="...", redirect_uri="...")

def load_token():
    ...  # load from your database, cache, or secrets manager

def save_token(tokens: dict):
    ...  # persist access_token, refresh_token, and expiry

def get_access_token() -> str:
    token = load_token()
    if token.is_expired():
        tokens = oauth.refresh_token(token.refresh_token)
        save_token(tokens)
        return tokens["access_token"]
    return token.access_token

client = XeroClient(
    access_token=get_access_token(),
    tenant_id=oauth.get_tenant_id(get_access_token()),
    access_token_provider=get_access_token,
)

On HTTP 401, the transport calls access_token_provider once and retries the request.

Migrating from raw HTTP

# Before
import requests

response = requests.get(
    f"https://api.xero.com/api.xro/2.0/Invoices/{invoice_id}",
    headers={
        "Authorization": f"Bearer {access_token}",
        "xero-tenant-id": tenant_id,
        "Accept": "application/json",
    },
)
response.raise_for_status()
invoice_data = response.json()["Invoices"][0]

# After
from xeroconnect import XeroClient

client = XeroClient(access_token=access_token, tenant_id=tenant_id)
invoice_data = client.invoices.get(invoice_id)["Invoices"][0]

Invoices

from datetime import date
from xeroconnect import XeroClient, date_on_or_after

# List invoices with filtering and pagination
invoices = client.invoices.list(
    where=date_on_or_after(date(2026, 1, 1)),
    order="Date DESC",
    page=1,
    page_size=100,
    offset=0,
)

# Get a single invoice
invoice = client.invoices.get("invoice-id")
print(invoice["Invoices"][0]["InvoiceNumber"])

# Update with full Xero envelope
result = client.invoices.update("invoice-id", {
    "Invoices": [{
        "InvoiceID": "invoice-id",
        "Status": "AUTHORISED",
    }],
})

# Update with bare invoice dict (auto-wrapped when wrap=True)
result = client.invoices.update(
    "invoice-id",
    {"InvoiceID": "invoice-id", "Status": "AUTHORISED"},
)

Pagination

Xero returns pagination metadata in response headers (for example x-page-count), not in the JSON body. List methods return the JSON body only. To read pagination headers, pass a custom httpx.Client and inspect the raw response via your own wrapper if needed.

Contacts

contacts = client.contacts.list(page=1, page_size=100)

contact = client.contacts.get("contact-id")
print(contact["Contacts"][0]["Name"])

Payments

payments = client.payments.list(page=1)

payments = client.payments.list(
    where='Status=="AUTHORISED"',
    order="Date DESC",
    page_size=50,
    offset=100,
)

payment = client.payments.get("payment-id")

Profit and Loss Report

report = client.reports.profit_and_loss(
    from_date="2026-01-01",
    to_date="2026-01-31",
)

# With optional parameters
report = client.reports.profit_and_loss(
    from_date="2026-01-01",
    to_date="2026-01-31",
    periods=3,
    timeframe="MONTH",
    tracking_category_id="category-id",
    tracking_option_id="option-id",
    standard_layout=True,
    payments_only=False,
)

Webhooks

Verify incoming webhook payloads using the signing key from your Xero app:

from xeroconnect import verify_signature

is_valid = verify_signature(
    payload=request.body,          # raw bytes
    signature_header=request.headers["x-xero-signature"],
    webhook_key="your-webhook-key",
)

Error Handling

XeroConnect raises typed exceptions with useful context:

from xeroconnect import (
    XeroClient,
    XeroAPIError,
    XeroAuthError,
    XeroConnectionError,
    XeroRateLimitError,
    XeroValidationError,
)

client = XeroClient(access_token="...", tenant_id="...")

try:
    invoices = client.invoices.list()
except XeroAuthError as e:
    print(f"Auth failed: {e.message}")
except XeroRateLimitError as e:
    print(f"Rate limited: {e.xero_message}, retry after {e.retry_after}s")
except XeroValidationError as e:
    print(f"Validation errors: {e.validation_errors}")
except XeroConnectionError as e:
    print(f"Network error: {e.message}")
except XeroAPIError as e:
    print(f"API error {e.status_code} on {e.endpoint}")
    print(f"Response: {e.response_body}")
Exception When
XeroConnectError Base exception
XeroConnectionError Network timeouts and connection failures
XeroAuthError OAuth failures and HTTP 401 (expired or invalid token)
XeroAPIError General API errors
XeroRateLimitError HTTP 429 rate limit (retry_after when provided)
XeroValidationError HTTP 400 validation errors (validation_errors when available)

Limitations

  • Focused scope — Only the endpoints listed above are supported. See xero-python for full API coverage.
  • No models — Responses are plain JSON dictionaries.
  • No token storage — You manage access and refresh tokens yourself.
  • No webhook event parsing — Signature verification only; event handling is your responsibility.
  • No sync engine — No background workers or database integration.
  • No framework bindings — No Django or FastAPI integration.
  • Limited write operations — Invoice update is supported; invoice create, contact create, and payment create are not.
  • No async client — Synchronous httpx only in v0.2.

Roadmap

  • Generic GET helper for additional read endpoints
  • Items API
  • Optional 429 retry with Retry-After
  • Additional reports (Balance Sheet, Trial Balance)
  • Invoice, contact, and payment create endpoints
  • Async client (httpx.AsyncClient)
  • PKCE OAuth flow

Requirements

  • Python >= 3.9
  • httpx >= 0.27

License

MIT License — see LICENSE for details.

Contributing

See CONTRIBUTING.md for setup, testing, and pull request guidelines.

Release history is in CHANGELOG.md.

Author

H. Ali — haxratali0@gmail.com

Metadata

Release files for xeroconnect 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for xeroconnect 0.2.0
File Size Uploaded Provenance Actions
xeroconnect-0.2.0.tar.gz 19.8 kB Without provenance Details for xeroconnect-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for xeroconnect 0.2.0
File Interpreter ABI Platform Actions
xeroconnect-0.2.0-py3-none-any.whl Python 3 none any Details for xeroconnect-0.2.0-py3-none-any.whl

Total release size: 36.5 kB

Release files / xeroconnect-0.2.0.tar.gz

Download URL xeroconnect-0.2.0.tar.gz
Size 19.8 kB
Tags Source
SHA-256 checksum
How to use checksums
ed96eff882e79d843b1cb924e46c01fe8beeaecb970fce3d052018f010ef4de6
BLAKE2b-256 checksum
How to use checksums
b0a1272f4e2153bc999680f4cec00d0353e901260ca7e5b7f258377d9e175f75
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.3

Release files / xeroconnect-0.2.0-py3-none-any.whl

Download URL xeroconnect-0.2.0-py3-none-any.whl
Size 16.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8eb10bf616087f44d38efe6917f094be9a9269b8e91ee3b9f99bb340925f1f4a
BLAKE2b-256 checksum
How to use checksums
f81f8ef38e36d8622dcd9ae78693b5daf1583394f30e540fd518d3827c9960f3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.3

Security for xeroconnect 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page