---
title: "Xero API: Complete Developer Guide for Integration (2026)"
url: "https://satvasolutions.com/blog/xero-api-integration-guide"
date: "2026-04-03T09:12:34-04:00"
modified: "2026-04-06T08:13:07-04:00"
author:
  name: "Chintan Prajapati"
  url: "https://satvasolutions.com"
categories:
  - "Accounting Integration"
tags:
  - "Xero API guide"
  - "Xero API integration guide"
  - "Xero OAuth 2.0 integration"
word_count: 2664
reading_time: "14 min read"
summary: "TABLE OF CONTENTS


 	Introduction
 	API Overview
 	Authentication
 	Key Endpoints
 	Rate Limits
 	Known Limitations
 	Webhooks
 	SDKs &amp; Libraries
 	Sandbox &amp; Testing
 	Code Exa..."
description: "Learn how to use the Xero API in 2026 with this complete guide. Covers OAuth 2.0, key endpoints, rate limits, webhooks, SDKs, and real integration examples f..."
keywords: "Xero API guide, Xero API integration guide, Xero OAuth 2.0 integration"
language: "en"
schema_type: "Article"
---

# Xero API: Complete Developer Guide for Integration (2026)

_Published: April 3, 2026_  
_Author: Chintan Prajapati_  

![Xero API integration connecting invoices, contacts, payments, bank transactions, and financial reports in cloud accounting](https://satvasolutions.com/wp-content/uploads/2026/04/xero-api-integration-invoices-contacts-payments-bank-transactions-reports-768x609.webp)

## Introduction

The Xero API is the backbone of every custom accounting integration built on the Xero platform.

Whether you are building a SaaS product that syncs invoices, an internal tool that automates bank reconciliation, or a reporting dashboard that pulls real-time financial data, the Xero API gives you programmatic access to the full accounting ledger.

This guide is the single reference you need in 2026. It covers **OAuth 2.0 authentication with PKCE**, every major endpoint across six Xero APIs, rate limits, known limitations (with workarounds), webhooks, official SDKs, sandbox testing, and production-ready code examples in Python and Node.js.

If you are evaluating [Xero integration services](/xero-integration-service) or building one yourself, start here.

### TL;DR — Quick Start in 5 Minutes

- **Protocol:** RESTful JSON over HTTPS
- **Auth:** OAuth 2.0 with PKCE (no more OAuth 1.0a)
- **Base URL:** `https://api.xero.com/api.xro/2.0/`
- **Rate Limit:** 60 calls/minute per tenant, 5,000/day
- **SDKs:** Official libraries for Python, Node.js, .NET, Java, Ruby, Go, PHP
- **First API Call:** Register an app at `developer.xero.com`, get your client ID, implement OAuth 2.0 flow, call `GET /Invoices`
- **Need help?** [Our Xero integration team](/xero-integration-service) builds production-grade integrations

## What is the Xero API? (Quick Overview)

![Xero API overview showing accounting, payroll, projects, assets, files, and bank feeds integration within cloud platform](https://satvasolutions.com/wp-content/uploads/2026/04/xero-api-overview-accounting-payroll-projects-assets-bankfeeds-integration.webp)Xero exposes **six distinct APIs**, each serving a different domain of accounting and business operations.

All follow the same RESTful conventions and share the same OAuth 2.0 authentication layer.

| API | Base URL | Purpose |
|---|---|---|
| **Accounting API** | `api.xero.com/api.xro/2.0/` | Core financials: invoices, contacts, payments, bank transactions, journals, chart of accounts, reports |
| **Assets API** | `api.xero.com/assets.xro/1.0/` | Fixed asset register, depreciation schedules, asset types |
| **Projects API** | `api.xero.com/projects.xro/2.0/` | Project tracking, time entries, project users, tasks |
| **Payroll API** | `api.xero.com/payroll.xro/2.0/` | Employee records, pay runs, leave, timesheets (region-specific: AU, NZ, UK) |
| **Files API** | `api.xero.com/files.xro/1.0/` | File uploads, folders, file associations to invoices/contacts |
| **BankFeeds API** | `api.xero.com/bankfeeds.xro/1.0/` | Push bank statement data into Xero for reconciliation |

All API requests require the `Xero-Tenant-Id` header, which identifies which Xero organization you are accessing.

A single OAuth 2.0 connection can be authorized for multiple tenants. Use the GET /connections endpoint to list all authorized tenants and retrieve their IDs.

**Request & Response Format:** All endpoints accept and return JSON by default. Set Content-Type: application/json and Accept: application/json headers.

Some endpoints also support XML, but JSON is recommended for new integrations.

## How Xero API Authentication Works (OAuth 2.0 Explained)

Since 2021, Xero requires **OAuth 2.0** for all API access. The older OAuth 1.0a flow is fully deprecated.

For server-side applications, use the standard Authorization Code flow.

For mobile or single-page apps, use the **Authorization Code flow with PKCE** (Proof Key for Code Exchange).

If you are building a native iOS app, see our detailed guide on [Xero OAuth 2.0 integration with PKCE for iOS](/blog/guide-on-xero-oauth-2-integration-with-native-ios-app-using-pkce).

![OAuth 2.0 PKCE authentication flow for Xero API showing user authorization, token exchange, and secure API data access](https://satvasolutions.com/wp-content/uploads/2026/04/xero-oauth2-pkce-authentication-flow-api-authorization-token-exchange.webp)

### OAuth 2.0 Flow Steps

1. **Register your app** at [developer.xero.com](https://developer.xero.com/app/manage). Choose Web App or PKCE depending on your architecture.
2. **Redirect the user** to the Xero authorization URL with your client_id, redirect_uri, requested scope, and a state parameter for CSRF protection.
3. **Exchange the authorization code** for an access token and refresh token by calling the token endpoint.
4. **Use the access token** in the Authorization: Bearer {token} header for all API calls.
5. **Refresh the token** before it expires (tokens last 30 minutes). Use the refresh token to get a new access token without user interaction.

### Scopes

Xero uses granular scopes to control API access. Request only the scopes you need:

| Scope | Access |
|---|---|
| openid profile email | User identity (required for all apps) |
| accounting.transactions | Invoices, bills, credit notes, payments, bank transactions |
| accounting.contacts | Contacts, contact groups |
| accounting.settings | Chart of accounts, tax rates, currencies, tracking categories |
| accounting.reports.read | Profit & Loss, Balance Sheet, Trial Balance, and other reports |
| accounting.journals.read | Journal entries (read-only) |
| accounting.attachments | File attachments on transactions |
| assets | Fixed assets API |
| projects | Projects API |
| payroll.* | Payroll API (region-specific scopes) |
| bankfeeds | BankFeeds API |
| files | Files API |

### Token Management Best Practices

- **Store refresh tokens securely** encrypted at rest, never in client-side code or version control.
- **Proactively refresh** tokens 2–3 minutes before the 30-minute expiry to avoid failed API calls.
- **Handle token revocation gracefully** if a user disconnects your app from Xero, your refresh token becomes invalid. Detect 401 responses and prompt re-authorization.
- **Use the offline_access scope** to receive a refresh token. Without it, you only get a short-lived access token.

## Key Accounting API Endpoints

The Accounting API is the most-used Xero API. Here are the essential endpoints every integration needs to know:

| Endpoint | Methods | Description | Pagination |
|---|---|---|---|
| /Invoices | GET, POST, PUT | Sales invoices and bills. Filter by status, date, contact. Supports `where` parameter for OData-style filtering. | Page-based (100 per page) |
| /Contacts | GET, POST, PUT | Customers, suppliers, and other contacts. Includes addresses, phone numbers, tax numbers. | Page-based (100 per page) |
| /Payments | GET, POST, PUT | Payments applied to invoices and bills. Link payments to bank accounts. | Page-based (100 per page) |
| /BankTransactions | GET, POST, PUT | Spend and receive money transactions. Used for direct bank entries not tied to invoices. | Page-based (100 per page) |
| /Journals | GET | System-generated journal entries. Read-only — you cannot create journals via this endpoint. See our guide on [Xero Journal API limitations](/blog/xero-journal-api-limitations-guide). | Offset-based (100 per page) |
| /ManualJournals | GET, POST, PUT | User-created manual journal entries. Supports multi-line journals with debit/credit pairs. | Page-based (100 per page) |
| /Accounts | GET, POST, PUT, DELETE | Chart of accounts. Retrieve, create, or modify account codes. See our resource on [downloading Xero chart of accounts](/blog/download-xero-chart-of-accounts-csv-pdf-with-nominal-code). | Not paginated (returns all) |
| /Items | GET, POST, PUT, DELETE | Inventory items and services. Includes purchase and sale unit prices. | Not paginated |
| /PurchaseOrders | GET, POST, PUT | Purchase orders with line items, delivery addresses, and status tracking. | Page-based (100 per page) |
| /Reports | GET | Financial reports: Profit and Loss, Balance Sheet, Trial Balance, Budget Summary, and more. Note: some reports have [known data extraction limitations](/blog/overcoming-xero-api-limitations-trial-balance-data-extraction-solutions). | Not paginated |

### Filtering and Querying

Most list endpoints support the where parameter for OData-style filtering. Examples:

```
GET /Invoices?where=Status=="AUTHORISED"&order=Date DESC
GET /Contacts?where=Name.Contains("Acme")
GET /BankTransactions?where=Date>=DateTime(2026,01,01)
GET /Invoices?where=Contact.ContactID==guid("abc-123-def")
```

Use the If-Modified-Since header to retrieve only records changed after a given timestamp essential for efficient sync workflows.

## Xero API Rate Limits (And How to Avoid 429 Errors)

Xero enforces strict rate limits to protect platform stability.

Understanding these limits is critical for production integrations, especially high-volume sync operations.

For a deeper breakdown with practical examples, read our guide on [Xero API sync limits and how many invoices you can sync per day](/blog/xero-invoices-sync-api-limits-guide).

| Limit Type | Threshold | Scope |
|---|---|---|
| **Per-minute limit** | 60 API calls per minute | Per tenant (organization) |
| **Daily limit** | 5,000 API calls per day | Per tenant |
| **App-wide limit** | 10,000 calls per minute | Across all tenants for your app |

![API rate limits and throttling dashboard showing error handling, retry strategy, request limits, and performance monitoring](https://satvasolutions.com/wp-content/uploads/2026/04/api-rate-limits-throttling-error-handling-retry-strategy-dashboard.webp)

### Handling 429 (Too Many Requests) Errors

When you hit a rate limit, Xero returns a `429` status code with a `Retry-After` header indicating how many seconds to wait. Your integration should:

1. **Implement exponential backoff:** start with the `Retry-After` value, then double the wait time on subsequent 429s.
2. **Use batching:** POST and PUT endpoints accept arrays of up to 50 items per request, reducing the number of API calls.
3. **Cache aggressively:** store chart of accounts, tax rates, and other slowly-changing data locally.
4. **Use If-Modified-Since:**for sync operations, only fetch records that changed since your last sync.

## Xero API Limitations (And Practical Workarounds)

No API is perfect. Xero has several well-documented limitations that developers encounter in production. We have published detailed guides on each:

- **Journal API is read-only:** The `/Journals` endpoint does not support POST or PUT. You can only read system journals.To create journal entries, use `/ManualJournals` instead. Filtering voided and reversal entries is also problematic see our full breakdown: [Xero Journal API Limitations: How to Filter Voided and Reversal Entries](/blog/xero-journal-api-limitations-guide).
- **Trial Balance data gaps:** The Trial Balance report via API may omit tracking category breakdowns and has limited date-range filtering compared to the Xero UI. Read our guide: [Overcoming Xero API Limitations for Trial Balance Data Extraction](/blog/overcoming-xero-api-limitations-trial-balance-data-extraction-solutions).
- **Aging reports are not available via API:** There is no direct endpoint for AR/AP aging reports. You must reconstruct aging data from invoice due dates and payment records. We detail the workaround here: [Xero API Aging Reports: AR/AP Data Extraction Limitations](/blog/xero-api-aging-reports-ar-ap-data-extraction-limitations).
- **Cash flow reports not directly available:** The Xero API does not expose a cash flow statement endpoint. You need to derive it from bank transactions and journal data. Our guide explains how: [How to Generate Cash Flow Reports from Xero When the API Does Not Offer It](/blog/xero-cash-flow-report-without-api).
- **Bank reconciliation API gap:** The API capabilities for reconciliation differ between Xero and competitors like QuickBooks. See our comparison: [Xero Has It. QuickBooks Does Not. The API Gap You Must Know](/blog/xero-vs-qbo-api-reconciliation-gap).
- **Foreign currency bank revaluation:** Calculating accurate balances for foreign currency accounts requires a custom approach. Our detailed guide: [Bank Revaluation in Xero: Calculating Accurate Balances for Foreign Currency Accounts Using API](/blog/how-to-calculate-bank-revaluation-in-xero-using-api).

If you are running into these limitations on a production integration, our [Xero integration service](/xero-integration-service) team has solved each of these problems at scale.

## Webhooks

Xero webhooks notify your application in real-time when data changes in a connected organization, eliminating the need for constant polling.

### Available Webhook Events

- **Contacts:** Create, Update
- **Invoices:** Create, Update
- **Quotes:** Create, Update
- **Manual Journals:** Create, Update
- **Payments:** Create, Update
- **Credit Notes:** Create, Update
- **Purchase Orders:** Create, Update

### Webhook Security

Every webhook subscription includes a **signing key**. Xero signs each payload with an HMAC-SHA256 hash using this key. Your endpoint must:

1. Compute the HMAC-SHA256 of the raw request body using your signing key.
2. Compare the computed hash with the `x-xero-signature` header value.
3. Return `200 OK` only if the signatures match. Return `401` otherwise.
4. Respond within **5 seconds** or Xero will retry (up to 7 retries with exponential backoff).

### Webhook Payload Format

```

          {
  "events": [
    {
      "resourceUrl": "https://api.xero.com/api.xro/2.0/Invoices/a1b2c3d4",
      "resourceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "eventDateUtc": "2026-03-15T10:30:00.000Z",
      "eventType": "Update",
      "eventCategory": "INVOICE",
      "tenantId": "your-tenant-id",
      "tenantType": "ORGANISATION"
    }
  ],
  "firstEventSequence": 1,
  "lastEventSequence": 1,
  "entropy": "random-string"
}
```

**Important:** The webhook payload only includes metadata (resource ID and type). You must make a follow-up API call to fetch the full resource data.

## SDKs & Libraries

Xero maintains official SDKs for all major languages. These handle OAuth 2.0 token management, request signing, serialization, and pagination automatically.

| Language | Package | GitHub Repository |
|---|---|---|
| Python | `xero-python` | [XeroAPI/xero-python](https://github.com/XeroAPI/xero-python) |
| Node.js | `xero-node` | [XeroAPI/xero-node](https://github.com/XeroAPI/xero-node) |
| .NET | `Xero.NetStandard.OAuth2` | [XeroAPI/Xero-NetStandard](https://github.com/XeroAPI/Xero-NetStandard) |
| Java | `xero-java` | [XeroAPI/Xero-Java](https://github.com/XeroAPI/Xero-Java) |
| Ruby | `xero-ruby` | [XeroAPI/xero-ruby](https://github.com/XeroAPI/xero-ruby) |
| Go | `xero-golang` | [XeroAPI/xero-golang](https://github.com/XeroAPI/xero-golang) |
| PHP | `xero-php-oauth2` | [XeroAPI/xero-php-oauth2](https://github.com/XeroAPI/xero-php-oauth2) |

For integrations that connect [multiple accounting platforms](/multiple-accounting-integrations) (Xero + QuickBooks + Sage), consider using a unified API abstraction layer rather than individual SDKs.

## Sandbox & Testing

Xero provides a **Demo Company** for every developer account a fully populated test organization with realistic sample data including invoices, contacts, bank accounts, and payroll records.

### Best Practices for Testing

- **Use the Demo Company** for initial development and automated testing. It resets periodically, so do not rely on specific record IDs persisting.
- **Create a dedicated trial organization** for integration testing that requires persistent data. Xero offers a free 30-day trial for each new org.
- **Implement idempotent operations** — use Xero-provided IDs (GUIDs) for updates rather than creating duplicate records.
- **Test rate-limit handling** by intentionally exceeding limits in your test environment and verifying your backoff logic works correctly.
- **Test token expiration** by forcing a token refresh and verifying your app handles the flow without user intervention.
- **Validate webhook signatures** using the Intent to Receive validation step during webhook subscription setup.

## Code Examples

### Creating an Invoice (Python)

```
from xero_python.api_client import ApiClient
from xero_python.accounting import AccountingApi, Invoice, Contact, LineItem
from datetime import date

# Assumes you have a configured api_client with valid OAuth 2.0 tokens
accounting_api = AccountingApi(api_client)

contact = Contact(
    name="Acme Corporation",
    email_address="billing@acme.com"
)

line_item = LineItem(
    description="Web Development Services - March 2026",
    quantity=40.0,
    unit_amount=150.00,
    account_code="200",
    tax_type="OUTPUT"
)

invoice = Invoice(
    type="ACCREC",
    contact=contact,
    line_items=[line_item],
    date=date(2026, 3, 15),
    due_date=date(2026, 4, 14),
    reference="INV-2026-0315",
    status="DRAFT"
)

created_invoice = accounting_api.create_invoices(
    xero_tenant_id="your-tenant-id",
    invoices={"Invoices": [invoice]}
)

print(f"Invoice created: {created_invoice.invoices[0].invoice_id}")
```

### Fetching Contacts with Pagination (Node.js)

```
const { XeroClient } = require("xero-node");

async function getAllContacts(xero, tenantId) {
  let allContacts = [];
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const response = await xero.accountingApi.getContacts(
      tenantId,
      null,  // ifModifiedSince
      null,  // where
      null,  // order
      null,  // iDs
      page,  // page number
      false  // includeArchived
    );

    const contacts = response.body.contacts;
    allContacts = allContacts.concat(contacts);

    // Xero returns 100 contacts per page
    hasMore = contacts.length === 100;
    page++;
  }

  console.log(`Fetched ${allContacts.length} contacts`);
  return allContacts;
}
```

### OAuth 2.0 Token Refresh

```
import requests

def refresh_xero_token(client_id, client_secret, refresh_token):
    """Refresh an expired Xero OAuth 2.0 access token."""

    token_url = "https://identity.xero.com/connect/token"

    response = requests.post(
        token_url,
        data={
            "grant_type": "refresh_token",
            "refresh_token": refresh_token,
            "client_id": client_id,
            "client_secret": client_secret,
        },
        headers={"Content-Type": "application/x-www-form-urlencoded"}
    )

    if response.status_code == 200:
        tokens = response.json()


# tokens["access_token"] - new access token (valid 30 min)


# tokens["refresh_token"] - new refresh token (store this!)


# tokens["expires_in"] - seconds until expiry (1800)
        return tokens
    else:
        raise Exception(f"Token refresh failed: {response.text}")
```

## Frequently Asked Questions

<dl class="faq-list"><dt class="faq-question">Is the Xero API free?</dt><dd class="faq-answer">Yes. Access to the Xero API itself is free. There are no per-call charges or separate API subscription fees. You only need a standard Xero subscription for each organization you connect to. Developer accounts and the Demo Company are also free. The cost is in your Xero subscription, not the API usage.</dd><dt class="faq-question">How do I get Xero API credentials?</dt><dd class="faq-answer">Go to developer.xero.com and create a free developer account. Then create a new app from the “My Apps” dashboard. You will receive a Client ID and Client Secret. For PKCE-based apps (mobile, SPA), you only need the Client ID. Store your Client Secret securely and never expose it in client-side code.</dd><dt class="faq-question">What are Xero API rate limits?</dt><dd class="faq-answer">Xero enforces three tiers of rate limiting: **60 calls per minute per tenant**, **5,000 calls per day per tenant**, and **10,000 calls per minute app-wide across all tenants**. When you exceed a limit, the API returns a **429 Too Many Requests** response with a Retry-After header. Use exponential backoff and batching to stay within limits.</dd><dt class="faq-question">Can I access bank feeds through the Xero API?</dt><dd class="faq-answer">Yes, but only through the dedicated **BankFeeds API**, which is separate from the Accounting API. It allows you to push bank statement data into Xero for reconciliation. This API requires additional approval from Xero, and your app must meet their security requirements.</dd><dt class="faq-question">How do I handle Xero OAuth 2.0 token expiration?</dt><dd class="faq-answer">Xero access tokens expire after **30 minutes**. To maintain uninterrupted access, request the **offline_access** scope to receive a refresh token. Refresh tokens should be renewed proactively before expiry using the `/connect/token` endpoint. Always store the latest refresh token securely. If refresh fails, the user must re-authorize the app.</dd><dt class="faq-question">Can I create invoices and other transactions via the API?</dt><dd class="faq-answer">Yes. The Accounting API supports full CRUD operations for invoices, bills, credit notes, payments, bank transactions, manual journals, contacts, and items. You can create records as **DRAFT** or **AUTHORISED**. Batch creation (up to 50 items per request) is also supported.</dd><dt class="faq-question">How do I integrate Xero with other accounting platforms?</dt><dd class="faq-answer">You can either build separate integrations for each platform like QuickBooks or Sage, or use a unified integration layer that normalizes multiple accounting APIs. This approach simplifies multi-platform support and enables scalable automation.</dd></dl>


---

_View the original post at: [https://satvasolutions.com/blog/xero-api-integration-guide](https://satvasolutions.com/blog/xero-api-integration-guide)_  
_Served as markdown by [Third Audience](https://github.com/third-audience) v3.5.4_  
_Generated: 2026-04-06 12:13:07 UTC_  
