---
# === IDENTITY ===
id: business/erp-integration/sage-intacct-api/2026
canonical_question: "What are the Sage Intacct REST API capabilities, rate limits, and session management patterns?"
aliases:
  - "Sage Intacct API rate limits and performance tiers"
  - "Sage Intacct REST vs XML Web Services API comparison"
  - "Sage Intacct OAuth 2.0 authentication and session management"
  - "How to authenticate and manage sessions with Sage Intacct API"
entity_type: erp_integration
domain: business > erp-integration > sage-intacct-api
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === SYSTEM PROFILE ===
systems:
  - name: "Sage Intacct"
    vendor: "Sage"
    version: "REST API v1 / XML API DTD 3.0"
    edition: "All editions (Essentials, Standard, Premium)"
    deployment: "cloud"
    api_surface: "REST (JSON) + XML Web Services"

# === VERIFICATION ===
last_verified: 2026-03-02
confidence: 0.88
version: 1.0
first_published: 2026-03-02

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: volatile
  last_breaking_change: "2023-01-15 -- HTTP GET requests to XML gateway blocked"
  next_review: 2026-08-29
  change_sensitivity: high

# === CONSTRAINTS ===
constraints:
  - "Performance Tier 1 (default): 100,000 API transactions/month -- overage charged at $0.15 per 10-transaction pack"
  - "Default concurrency: 2 concurrent connections per tenant (company ID) -- higher tiers available via CSM"
  - "XML API session timeout: 30 minutes of inactivity -- no keep-alive mechanism"
  - "REST API requires OAuth 2.0 -- XML API uses sender ID + session-based auth (different auth models)"
  - "No free developer sandbox -- paid Sage Intacct instance required for testing"
  - "REST API has limited object coverage -- many modules still require XML API (project accounting, fixed assets, custom objects)"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "User needs Sage 200 or Sage 300 API, not Sage Intacct"
    use_instead: "N/A -- different Sage product lines with separate API documentation"
  - condition: "User needs Sage Construction Management API"
    use_instead: "N/A -- Sage Construction Management uses a separate REST API (api-ext.sagecm.intacct.com)"
  - condition: "User only needs the Sage Intacct SDK for .NET, Node.js, or PHP"
    use_instead: "N/A -- SDKs wrap the XML API; see developer.intacct.com for SDK docs"

# === AGENT HINTS ===
inputs_needed:
  - key: integration_pattern
    question: "What integration pattern do you need?"
    type: choice
    options:
      - "real-time sync (individual records, <1s latency)"
      - "batch/bulk (scheduled, high volume)"
      - "event-driven (webhook, CDC, platform events)"
      - "file-based (CSV/XML import/export)"
  - key: data_volume
    question: "What's your daily data volume?"
    type: choice
    options:
      - "< 1,000 records/day"
      - "1,000-100,000 records/day"
      - "> 100,000 records/day"
  - key: direction
    question: "What's the data flow direction?"
    type: choice
    options:
      - "inbound (writing to Sage Intacct)"
      - "outbound (reading from Sage Intacct)"
      - "bidirectional sync"

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/business/erp-integration/sage-intacct-api/2026"
suggested_citation: "Source: knowledgelib.io -- AI Knowledge Library (verified 2026-03-02)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to:
    - id: "business/erp-integration/netsuite-rest-api/2026"
      label: "NetSuite REST API capabilities -- comparable mid-market ERP API"
  solves:
    - id: "business/erp-integration/ap-automation-integration-playbook/2026"
      label: "AP automation integration playbook using Sage Intacct"
  alternative_to:
    - id: "business/erp-integration/quickbooks-online-api/2026"
      label: "QuickBooks Online API -- alternative for smaller organizations"
  often_confused_with:
    - id: "business/erp-integration/sage-200-api/2026"
      label: "Sage 200 API -- different product with separate authentication and endpoints"

# === SOURCES ===
sources:
  - id: src1
    title: "XML Web Services Overview"
    author: Sage
    url: https://developer.intacct.com/web-services/
    type: official_docs
    published: 2025-12-01
    reliability: authoritative
  - id: src2
    title: "API Sessions"
    author: Sage
    url: https://developer.intacct.com/api/company-console/api-sessions/
    type: official_docs
    published: 2025-12-01
    reliability: authoritative
  - id: src3
    title: "Sage Intacct REST API Documentation"
    author: Sage
    url: https://developer.sage.com/intacct/docs/
    type: official_docs
    published: 2025-12-01
    reliability: authoritative
  - id: src4
    title: "API Transaction Volume, Concurrency, and Scaling"
    author: Sage
    url: https://developer.sage.com/intacct/docs/developer-portal/api-essentials/txn-volume-concurrency-scaling/
    type: official_docs
    published: 2025-10-01
    reliability: authoritative
  - id: src5
    title: "Error Handling -- Sage Intacct Web Services"
    author: Sage
    url: https://developer.intacct.com/web-services/error-handling/
    type: official_docs
    published: 2025-12-01
    reliability: authoritative
  - id: src6
    title: "Sage Intacct API Limits: What Are They? Why Should You Care?"
    author: RKL eSolutions
    url: https://www.rklesolutions.com/blog/sage-intacct-api-limits-what-are-they-why-should-you-care
    type: technical_blog
    published: 2025-06-15
    reliability: moderate_high
  - id: src7
    title: "Sage Intacct Performance Tiers White Paper"
    author: Sage
    url: https://developer.sage.com/intacct/performance-tiers/white-paper-performance-tiers.pdf
    type: official_docs
    published: 2025-01-01
    reliability: authoritative
  - id: src8
    title: "Deep-Dive Developer Guide to Building a Sage Intacct API Integration"
    author: Knit
    url: https://www.getknit.dev/blog/sage-intacct-api-integration-guide-in-depth
    type: technical_blog
    published: 2025-08-01
    reliability: moderate_high
---

# Sage Intacct API: REST & XML Web Services Capabilities, Rate Limits, and Session Management

## TL;DR

- **Bottom line**: Sage Intacct offers two API surfaces -- a modern REST API (JSON/OAuth 2.0) and a legacy XML Web Services API (XML/session-based). The REST API is recommended for new development, but the XML API remains essential for modules not yet covered by REST (project accounting, fixed assets, custom objects). [src3]
- **Key limit**: 100,000 API transactions/month on Performance Tier 1 (default); 2 concurrent connections per tenant. Overage charged at $0.15 per 10-transaction pack. [src4, src6]
- **Watch out for**: Each create/update/delete counts as a separate transaction even when batched -- a 500-line journal entry with header is 1 transaction, but 500 individual vendor creates are 500 transactions. [src6]
- **Best for**: Mid-market financial management -- GL, AP, AR, cash management, and order management integrations with up to ~3,000 transactions/day on Tier 1.
- **Authentication**: REST API uses OAuth 2.0 (authorization code or client credentials grant); XML API uses sender ID + company credentials with session-based auth (getAPISession). [src1, src3]

## System Profile

Sage Intacct is a cloud-native financial management system widely used in mid-market organizations ($10M--$1B revenue). It offers two API surfaces with different authentication models, capabilities, and maturity levels. The REST API (v1) was introduced to modernize integrations and is where Sage invests new feature development. The XML Web Services API (DTD 3.0) provides comprehensive access to the full Sage Intacct feature set and remains the primary integration surface for complex financial transactions. Sage provides official SDKs for the XML API in .NET, Node.js, and PHP. [src1, src3]

| Property | Value |
|---|---|
| **Vendor** | Sage |
| **System** | Sage Intacct |
| **API Surface** | REST API v1 (JSON) + XML Web Services (DTD 3.0) |
| **Current API Version** | REST v1 / XML DTD 3.0 |
| **Editions Covered** | Essentials, Standard, Premium (all editions) |
| **Deployment** | Cloud (multi-tenant SaaS) |
| **API Docs URL** | [REST API](https://developer.sage.com/intacct/docs/) / [XML API](https://developer.intacct.com/api/) |
| **Status** | REST: GA (expanding coverage) / XML: GA (full coverage, maintenance) |

## API Surfaces & Capabilities

| API Surface | Protocol | Best For | Max Records/Request | Rate Limit | Real-time? | Bulk? |
|---|---|---|---|---|---|---|
| REST API v1 | HTTPS/JSON | Modern integrations, GL, AP, AR, cash management | Paginated (varies by object) | 100K txn/month (Tier 1) | Yes | Limited |
| XML Web Services (DTD 3.0) | HTTPS/XML | Full feature set, complex transactions, project accounting, fixed assets | 100 records per manipulation recommended | 100K txn/month (Tier 1), 2 concurrent | Yes | Yes (batched functions) |
| XML Web Services (Legacy DTD 2.1) | HTTPS/XML | Legacy integrations only | Same as DTD 3.0 | Shared with DTD 3.0 | Yes | Yes |
| Smart Events/Triggers | Platform events | Event-driven automation within Sage Intacct | N/A | Platform-dependent | Yes | N/A |
| CSV Import | File upload (UI/API) | Bulk data loading, journal entries, transactions | Varies by object | N/A | No | Yes |

[src1, src3]

## Rate Limits & Quotas

### Per-Request Limits

| Limit Type | Value | Applies To | Notes |
|---|---|---|---|
| Max records per query | 1,000 (recommended) | XML API readByQuery/query | Use pagination (resultId) for larger sets [src1] |
| Max records per manipulation | 100 (recommended) | XML API create/update/delete | Larger batches risk 15-min timeout [src1] |
| Max request processing time | 15 minutes | XML API | Gateway returns timeout error; backend may still process [src1] |
| Max concurrent connections (default) | 2 per tenant | Both APIs | 3rd request held ~30s then rejected [src4] |
| Max concurrent connections (per-app) | 6 (app) / 8 (company total) | Higher tiers | Contractual; contact CSM to upgrade [src4] |

### Rolling / Daily Limits

| Limit Type | Value | Window | Edition Differences |
|---|---|---|---|
| API transactions (Tier 1) | 100,000 | Monthly (calendar) | Default for all editions; not blocked on overage, but charged [src6, src7] |
| API transactions (Tier 2-4) | Up to 2,500,000 | Monthly (calendar) | Premium tiers with higher concurrency; contact CSM for pricing [src7] |
| API transactions (Tier 5) | Custom / unlimited | Monthly (calendar) | Enterprise agreement; custom pricing [src7] |
| Overage fee (Tier 1) | $0.15 per 10-txn pack | Per-month | Charged retroactively; not blocked [src6] |

[src4, src6, src7]

### Transaction Counting Rules

| Operation | Transaction Count | Notes |
|---|---|---|
| Single create/update/delete | 1 | Each API function call counted individually [src6] |
| Header + line items (e.g., AP bill with 50 lines) | 1 | Header objects count as 1 regardless of line items [src6] |
| Batch of 100 vendor creates in one request | 100 | Each function call is a separate transaction [src6] |
| query/readByQuery | 1 | Each query counts as 1 transaction [src6] |
| getAPISession | 1 | Session creation is a transaction [src6] |
| Sender IDs counted | ajax, SMART_EVENT, PLATFORMAPI, Web Services Developer License sender IDs | Only these sender IDs count toward tier limits [src6] |

## Authentication

### REST API Authentication (OAuth 2.0)

| Flow | Use When | Token Lifetime | Refresh? | Notes |
|---|---|---|---|---|
| Authorization Code | User-context operations, interactive apps | JWT-based (configurable) | Yes | Standard OAuth 2.0 flow; requires redirect URI [src3] |
| Client Credentials | Server-to-server, unattended integrations | JWT-based (configurable) | Yes | Recommended for daemon/service integrations [src3] |

REST API OAuth 2.0 endpoints: [src3]
- **Authorization**: `https://api.intacct.com/ia/api/v1/oauth2/authorize`
- **Token**: `https://api.intacct.com/ia/api/v1/oauth2/token`
- **Base URL**: `https://api.intacct.com/ia/api/v1/`

### XML API Authentication (Session-Based)

| Flow | Use When | Token Lifetime | Refresh? | Notes |
|---|---|---|---|---|
| Login auth (company credentials) | Initial session creation only | Single request | No | Do NOT use for repeated requests; use getAPISession instead [src1] |
| Session auth (getAPISession) | All repeated API calls | 30 min inactivity timeout | Auto-extends on activity | Preferred; returns session ID + unique endpoint [src1, src2] |

XML API endpoint: `https://api.intacct.com/ia/xml/xmlgw.phtml` [src1]

### Authentication Gotchas

- **Dual authentication required for XML API**: Every XML request needs both Web Services credentials (sender ID + password) in the `<control>` block AND company credentials (session ID or login) in the `<authentication>` block. Missing either one fails silently with a generic error. [src1]
- **Sender ID must be authorized per company**: Your Web Services sender ID must be explicitly authorized in each Sage Intacct company's Web Services Authorizations (Company > Admin > Web Services Authorizations). Without authorization, requests return "Sender ID not authorized." [src1]
- **Session endpoints differ per session**: getAPISession returns a unique endpoint URL (e.g., `https://na12.intacct.com/ia/xml/xmlgw.phtml`). You MUST use this endpoint for subsequent requests -- not the generic `api.intacct.com` gateway. [src2]
- **REST and XML API use completely different auth models**: You cannot use an XML session ID with the REST API, or an OAuth bearer token with the XML API. They are separate authentication systems. [src1, src3]
- **No service accounts in the traditional sense**: For XML API, create a dedicated Web Services user (Company > Web Services Users) rather than using a named user's credentials. [src1]

## Constraints

- **REST API coverage is incomplete** -- project accounting, fixed assets, custom objects, consolidations, and several other modules are only accessible via the XML API. Check the REST API object mapping before committing to REST-only. [src3]
- **2 concurrent connections is the default** -- exceeding this causes the 3rd request to queue for ~30 seconds, then fail. Most mid-market integrations hit this limit quickly with even modest parallelism. [src4]
- **100,000 transactions/month on Tier 1** -- this sounds generous but counts every create/update/delete/query individually. A nightly sync of 1,000 invoices with 10 API calls each burns 10,000 transactions per night (300,000/month). [src6, src7]
- **No free sandbox environment** -- testing requires a paid Sage Intacct instance. No distinction between developer and production keys. [src8]
- **XML API session timeout of 30 minutes** -- long-running batch processes must implement session refresh logic or risk mid-process failures with no automatic retry. [src1, src2]
- **HTTP GET requests to XML gateway are blocked** -- as of January 15, 2023, only POST requests are accepted at the XML gateway endpoint. [src1]
- **Line items cannot be updated directly** -- line items of a transaction (e.g., bill lines) are owned by the header object. You must update the entire header to modify line items. [src1]

## Integration Pattern Decision Tree

```
START -- User needs to integrate with Sage Intacct
|-- Which API surface?
|   |-- Module covered by REST API? (GL, AP, AR, Cash Management, Order Entry)
|   |   |-- YES, and using OAuth 2.0 is acceptable
|   |   |   --> REST API v1 (recommended for new development) [src3]
|   |   |-- YES, but need session-based auth or XML batching
|   |       --> XML API DTD 3.0 [src1]
|   |-- Module NOT covered by REST? (Project Accounting, Fixed Assets, Custom Objects)
|       --> XML API DTD 3.0 (required) [src3]
|-- What's the integration pattern?
|   |-- Real-time (individual records, <1s latency)
|   |   |-- Data volume < 100 records/operation?
|   |   |   --> REST API (single record CRUD) or XML API (single function) [src1]
|   |   |-- Need to minimize transaction count?
|   |       --> XML API with multiple functions per request [src1]
|   |-- Batch/Bulk (scheduled, high volume)
|   |   |-- < 100 records per batch?
|   |   |   --> XML API with transaction="true" (all-or-nothing) [src1]
|   |   |-- 100-1,000 records?
|   |   |   --> XML API with chunked batches + pagination [src1]
|   |   |-- > 1,000 records?
|   |       --> XML API with chunked batches + monitor Tier usage [src6]
|   |-- Event-driven
|   |   --> Smart Events within Sage Intacct + XML API for external triggers
|   |-- File-based (CSV import)
|       --> CSV import via UI or API-driven journal entry import
|-- Concurrency needs?
|   |-- <= 2 concurrent requests --> Default Tier 1
|   |-- > 2 concurrent requests --> Upgrade performance tier via CSM [src4]
|-- Monthly transaction budget?
    |-- < 100,000 --> Tier 1 (included)
    |-- 100,000-2,500,000 --> Tier 2-4 (paid upgrade) [src7]
    |-- > 2,500,000 --> Tier 5 (custom enterprise agreement) [src7]
```

## Quick Reference

| Operation | Method | Endpoint | Payload | Notes |
|---|---|---|---|---|
| Create session (XML) | POST | `https://api.intacct.com/ia/xml/xmlgw.phtml` | XML (getAPISession) | Returns sessionid + unique endpoint [src2] |
| Query records (XML) | POST | `{session_endpoint}` | XML (readByQuery/query) | Max 1,000 recommended; use resultId for pagination [src1] |
| Create record (XML) | POST | `{session_endpoint}` | XML (create function) | Each function = 1 transaction [src1] |
| Update record (XML) | POST | `{session_endpoint}` | XML (update function) | Must include header for line-item changes [src1] |
| OAuth token (REST) | POST | `https://api.intacct.com/ia/api/v1/oauth2/token` | JSON | Returns JWT bearer token [src3] |
| List objects (REST) | GET | `https://api.intacct.com/ia/api/v1/objects/{module}/{object}` | N/A | Bearer token in Authorization header [src3] |
| Create record (REST) | POST | `https://api.intacct.com/ia/api/v1/objects/{module}/{object}` | JSON | Bearer token; Content-Type: application/json [src3] |
| Get bank account (REST) | GET | `https://api.intacct.com/ia/api/v1/objects/cash-management/bank-account/{key}` | N/A | Bearer token [src8] |

## Step-by-Step Integration Guide

### 1. Obtain Web Services credentials (XML API)

Register as a Sage Intacct developer or obtain a Web Services Developer License through your Sage Intacct subscription. You will receive a sender ID and sender password. These are NOT the same as your company login credentials. [src1]

```plaintext
Required credentials:
- Sender ID: Your developer/partner sender ID
- Sender Password: Associated password for the sender ID
- Company ID: Your Sage Intacct company ID
- User ID: A Web Services user (Company > Web Services Users)
- User Password: The Web Services user's password
```

**Verify**: Log into Sage Intacct UI > Company > Admin > Web Services Authorizations. Confirm your sender ID appears in the authorized list.

### 2. Create an API session (XML API)

Use getAPISession to obtain a session ID and unique endpoint. Always isolate getAPISession in its own request -- do not combine it with other function calls. [src1, src2]

```xml
<?xml version="1.0" encoding="UTF-8"?>
<request>
  <control>
    <senderid>YOUR_SENDER_ID</senderid>
    <password>YOUR_SENDER_PASSWORD</password>
    <controlid>session-request-1</controlid>
    <uniqueid>false</uniqueid>
    <dtdversion>3.0</dtdversion>
  </control>
  <operation>
    <authentication>
      <login>
        <userid>YOUR_WS_USER_ID</userid>
        <companyid>YOUR_COMPANY_ID</companyid>
        <password>YOUR_WS_USER_PASSWORD</password>
      </login>
    </authentication>
    <content>
      <function controlid="get-session">
        <getAPISession/>
      </function>
    </content>
  </operation>
</request>
```

**Verify**: Response contains `<sessionid>` and `<endpoint>` elements. Store both -- use the returned endpoint (not api.intacct.com) for all subsequent calls.

### 3. Make API calls using session (XML API)

Replace login authentication with the session ID from step 2. Send all requests to the unique endpoint returned by getAPISession. [src1, src2]

```xml
<?xml version="1.0" encoding="UTF-8"?>
<request>
  <control>
    <senderid>YOUR_SENDER_ID</senderid>
    <password>YOUR_SENDER_PASSWORD</password>
    <controlid>query-vendors-1</controlid>
    <uniqueid>false</uniqueid>
    <dtdversion>3.0</dtdversion>
  </control>
  <operation>
    <authentication>
      <sessionid>YOUR_SESSION_ID</sessionid>
    </authentication>
    <content>
      <function controlid="query-1">
        <query>
          <object>VENDOR</object>
          <select>
            <field>VENDORID</field>
            <field>NAME</field>
            <field>STATUS</field>
          </select>
          <pagesize>100</pagesize>
        </query>
      </function>
    </content>
  </operation>
</request>
```

**Verify**: Response `<status>success</status>` with vendor records in `<data>` element.

### 4. Handle pagination (XML API)

When a query returns more records than the page size, the response includes a `<resultId>` element. Use `readMore` with this ID to fetch the next page. [src1]

```xml
<function controlid="read-more-1">
  <readMore>
    <resultId>YOUR_RESULT_ID_FROM_PREVIOUS_RESPONSE</resultId>
  </readMore>
</function>
```

**Verify**: Response contains next page of records or empty `<data>` when all records are retrieved.

### 5. Authenticate with OAuth 2.0 (REST API)

For the REST API, obtain an OAuth 2.0 bearer token from the token endpoint. Use client credentials grant for server-to-server integrations. [src3]

```bash
# Request OAuth 2.0 token (client credentials)
curl -X POST "https://api.intacct.com/ia/api/v1/oauth2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"
```

**Verify**: Response contains `access_token` (JWT), `token_type: "Bearer"`, and `expires_in`.

### 6. Implement error handling and retries

Handle rate limit errors (GW-0010), session timeouts, and mid-process failures with appropriate retry logic. [src5]

```python
# Input:  XML request payload, session info
# Output: Successful API response or raised exception

import time
import requests

MAX_RETRIES = 5
BACKOFF_BASE = 2

def call_intacct_api(endpoint, xml_payload, retry=0):
    """Call Sage Intacct XML API with retry logic."""
    response = requests.post(
        endpoint,
        data=xml_payload,
        headers={"Content-Type": "application/xml"},
        timeout=900  # 15 min max
    )

    if response.status_code == 429 or "GW-0010" in response.text:
        if retry < MAX_RETRIES:
            wait = BACKOFF_BASE ** retry
            time.sleep(wait)
            return call_intacct_api(endpoint, xml_payload, retry + 1)
        raise Exception("Rate limit exceeded after max retries")

    if "XL03000006" in response.text:
        raise Exception("Authentication failed -- check sender ID and credentials")

    return response
```

**Verify**: Function returns successful response or raises specific exception after exhausting retries.

## Code Examples

### Python: Query AP bills via XML API

```python
# Input:  sender_id, sender_password, session_id, endpoint
# Output: List of AP bill records

import requests
import xml.etree.ElementTree as ET

def query_ap_bills(endpoint, sender_id, sender_pwd, session_id, page_size=100):
    """Query AP bills from Sage Intacct XML API."""
    xml_request = f"""<?xml version="1.0" encoding="UTF-8"?>
    <request>
      <control>
        <senderid>{sender_id}</senderid>
        <password>{sender_pwd}</password>
        <controlid>query-bills</controlid>
        <uniqueid>false</uniqueid>
        <dtdversion>3.0</dtdversion>
      </control>
      <operation>
        <authentication>
          <sessionid>{session_id}</sessionid>
        </authentication>
        <content>
          <function controlid="query-bills-1">
            <query>
              <object>APBILL</object>
              <select>
                <field>RECORDNO</field>
                <field>VENDORID</field>
                <field>TOTALDUE</field>
                <field>WHENCREATED</field>
                <field>STATE</field>
              </select>
              <pagesize>{page_size}</pagesize>
            </query>
          </function>
        </content>
      </operation>
    </request>"""

    resp = requests.post(
        endpoint,
        data=xml_request,
        headers={"Content-Type": "application/xml"},
        timeout=300
    )
    resp.raise_for_status()

    root = ET.fromstring(resp.text)
    status = root.find(".//result/status")
    if status is not None and status.text == "success":
        records = root.findall(".//data/APBILL")
        return [
            {child.tag: child.text for child in record}
            for record in records
        ]
    else:
        error = root.find(".//errormessage/error/description2")
        raise Exception(f"API error: {error.text if error is not None else 'Unknown'}")
```

### JavaScript/Node.js: Create vendor via XML API with SDK

```javascript
// Input:  Sage Intacct credentials, vendor data
// Output: Created vendor record number

// npm install @anthropic-ai/sdk @sage-intacct/intacct-sdk (v3.x)
const IA = require("@sage-intacct/intacct-sdk");

async function createVendor(vendorData) {
  const client = new IA.OnlineClient({
    senderId: process.env.INTACCT_SENDER_ID,
    senderPassword: process.env.INTACCT_SENDER_PASSWORD,
    companyId: process.env.INTACCT_COMPANY_ID,
    userId: process.env.INTACCT_USER_ID,
    userPassword: process.env.INTACCT_USER_PASSWORD,
  });

  const create = new IA.Functions.AccountsPayable.VendorCreate();
  create.vendorId = vendorData.id;
  create.vendorName = vendorData.name;
  create.controlId = `create-vendor-${Date.now()}`;

  try {
    const response = await client.execute(create);
    console.log("Vendor created, RECORDNO:", response.getResults()[0].data);
    return response.getResults()[0].data;
  } catch (error) {
    console.error("Failed:", error.message);
    throw error;
  }
}
```

### cURL: Quick session creation and query test

```bash
# Input:  sender_id, sender_password, company_id, user_id, user_password
# Output: Session ID + vendor list

# Step 1: Create API session
curl -s -X POST "https://api.intacct.com/ia/xml/xmlgw.phtml" \
  -H "Content-Type: application/xml" \
  -d '<?xml version="1.0" encoding="UTF-8"?>
  <request>
    <control>
      <senderid>YOUR_SENDER_ID</senderid>
      <password>YOUR_SENDER_PASSWORD</password>
      <controlid>session-1</controlid>
      <uniqueid>false</uniqueid>
      <dtdversion>3.0</dtdversion>
    </control>
    <operation>
      <authentication>
        <login>
          <userid>YOUR_USER_ID</userid>
          <companyid>YOUR_COMPANY_ID</companyid>
          <password>YOUR_USER_PASSWORD</password>
        </login>
      </authentication>
      <content>
        <function controlid="get-session">
          <getAPISession/>
        </function>
      </content>
    </operation>
  </request>'

# Expected: Response XML with <sessionid> and <endpoint> elements
# Extract sessionid and endpoint from response, then:

# Step 2: Query vendors using session
curl -s -X POST "RETURNED_ENDPOINT_URL" \
  -H "Content-Type: application/xml" \
  -d '<?xml version="1.0" encoding="UTF-8"?>
  <request>
    <control>
      <senderid>YOUR_SENDER_ID</senderid>
      <password>YOUR_SENDER_PASSWORD</password>
      <controlid>query-1</controlid>
      <uniqueid>false</uniqueid>
      <dtdversion>3.0</dtdversion>
    </control>
    <operation>
      <authentication>
        <sessionid>YOUR_SESSION_ID</sessionid>
      </authentication>
      <content>
        <function controlid="query-vendors">
          <query>
            <object>VENDOR</object>
            <select>
              <field>VENDORID</field>
              <field>NAME</field>
            </select>
            <pagesize>10</pagesize>
          </query>
        </function>
      </content>
    </operation>
  </request>'
```

## Data Mapping

### API Object Mapping (REST to XML)

| REST API Object | XML API Object | Module | Notes |
|---|---|---|---|
| `/objects/general-ledger/account` | GLACCOUNT | General Ledger | Full CRUD both APIs [src3] |
| `/objects/general-ledger/journal-entry` | GLBATCH | General Ledger | REST uses "journal-entry"; XML uses "GLBATCH" [src3] |
| `/objects/accounts-payable/bill` | APBILL | Accounts Payable | Full CRUD both APIs [src3] |
| `/objects/accounts-payable/vendor` | VENDOR | Accounts Payable | Full CRUD both APIs [src3] |
| `/objects/accounts-receivable/invoice` | ARINVOICE | Accounts Receivable | Full CRUD both APIs [src3] |
| `/objects/cash-management/bank-account` | BANKACCOUNT | Cash Management | REST coverage available [src3] |
| N/A (REST not available) | PROJECT | Project Accounting | XML API only [src3] |
| N/A (REST not available) | FIXEDASSET | Fixed Assets | XML API only [src3] |
| N/A (REST not available) | Custom objects | Platform Services | XML API only [src3] |

### Data Type Gotchas

- **Date formats differ between APIs**: REST API uses ISO 8601 (`2026-03-02`); XML API uses `MM/DD/YYYY` format for date fields and separate year/month/day elements for some operations. Always verify per-field. [src1, src3]
- **Currency amounts**: XML API stores amounts as strings in some contexts; always parse to decimal. REST API returns numeric JSON types. Multi-currency orgs must specify transaction currency explicitly. [src1]
- **Boolean fields**: XML API uses strings `"true"`/`"false"` or `"T"`/`"F"` depending on the object. REST API uses JSON `true`/`false`. Check per-object documentation. [src1]
- **Record numbers vs IDs**: Sage Intacct uses both `RECORDNO` (system-generated integer) and user-defined IDs (e.g., `VENDORID`). XML API often requires `RECORDNO` for updates; REST API may accept either. [src1, src3]

## Error Handling & Failure Points

### Common Error Codes

| Code | Meaning | Cause | Resolution |
|---|---|---|---|
| XL03000003 | XML Parse schema error | Malformed XML, DTD mismatch | Validate XML against DTD 3.0 schema; check for encoding issues [src5] |
| XL03000006 | Authentication failure | Invalid sender ID, wrong credentials, or unauthorized sender | Verify sender ID is authorized in target company; check Web Services Authorizations [src5] |
| GW-0010 | Rate limit / throttling | Too many concurrent requests | Implement exponential backoff; reduce concurrency to below tenant limit [src5] |
| GW-0011 | Invalid request | Malformed gateway request | Validate XML structure; ensure POST method (not GET) [src5] |
| BL01001973 | Object not found | Referenced record does not exist (e.g., invalid VENDORID) | Verify record exists with a query before create/update operations [src5] |
| BL34000061 | Session expired | Session timed out after 30 minutes of inactivity | Create new session via getAPISession; implement session refresh [src2, src5] |
| 429 | Too Many Requests | REST API rate limit exceeded | Exponential backoff with jitter; respect Retry-After header [src3] |

[src5]

### Failure Points in Production

- **Session timeout during long-running batches**: A batch job processing >30 minutes between API calls loses its session. Fix: `Implement session heartbeat by making a lightweight API call (e.g., inspect) every 20 minutes, or create a new session before each batch chunk.` [src1, src2]
- **Concurrent connection rejection**: With only 2 default concurrent connections, parallel integration processes (e.g., AP sync + AR sync running simultaneously) cause GW-0010 errors. Fix: `Serialize API calls with a queue, or upgrade performance tier. Use a connection pool with max 2 connections.` [src4]
- **Transaction count explosion**: Nightly syncs that create/update records individually burn through 100K monthly transactions in days. Fix: `Batch related operations into single requests where possible. Use XML API's multi-function requests to group operations. Monitor usage at Company > Admin > Usage Insights > API Usage.` [src6]
- **Mid-process failures with no idempotency**: If a request times out at 15 minutes, the backend may have partially processed it. Resubmitting creates duplicates. Fix: `Set uniqueid=true in the control block and use consistent controlid values. This enables idempotent retry.` [src5]
- **XML encoding issues with special characters**: Ampersands, angle brackets, and non-ASCII characters in data values break XML parsing. Fix: `Always XML-encode data values. Use CDATA sections for free-text fields. The SDKs handle this automatically.` [src5]

## Anti-Patterns

### Wrong: Using login authentication for every request

```xml
<!-- BAD -- creates a new session for every single API call, wasting transactions -->
<operation>
  <authentication>
    <login>
      <userid>ws_user</userid>
      <companyid>MYCOMPANY</companyid>
      <password>mypassword</password>
    </login>
  </authentication>
  <content>
    <function controlid="q1"><query>...</query></function>
  </content>
</operation>
```

### Correct: Create session once, reuse for all requests

```xml
<!-- GOOD -- one getAPISession, then reuse sessionid for all subsequent calls -->
<!-- Step 1: Get session (one-time) -->
<function controlid="get-session"><getAPISession/></function>

<!-- Step 2+: Use session ID for all calls -->
<operation>
  <authentication>
    <sessionid>REUSED_SESSION_ID</sessionid>
  </authentication>
  <content>
    <function controlid="q1"><query>...</query></function>
  </content>
</operation>
```

### Wrong: Sending one function per request for bulk operations

```python
# BAD -- 500 separate HTTP requests for 500 vendor creates = 500 transactions + massive latency
for vendor in vendors:
    response = send_xml_request(create_vendor_xml(vendor))
```

### Correct: Batch multiple functions in a single request

```python
# GOOD -- one HTTP request with multiple functions = still 500 transactions but far less latency
functions_xml = "".join([create_vendor_xml(v) for v in vendors[:100]])  # max 100
request_xml = wrap_in_operation(functions_xml, transaction=True)
response = send_xml_request(request_xml)
# transaction=true means all-or-nothing rollback on failure
```

### Wrong: Ignoring the unique session endpoint

```python
# BAD -- always sending to the generic gateway instead of session-specific endpoint
SESSION_ENDPOINT = "https://api.intacct.com/ia/xml/xmlgw.phtml"  # wrong!
response = requests.post(SESSION_ENDPOINT, data=xml_payload)
```

### Correct: Use the endpoint returned by getAPISession

```python
# GOOD -- use the session-specific endpoint for lower latency and correct routing
session_info = create_session()  # returns sessionid + endpoint
SESSION_ENDPOINT = session_info["endpoint"]  # e.g., https://na12.intacct.com/ia/xml/xmlgw.phtml
response = requests.post(SESSION_ENDPOINT, data=xml_payload)
```

## Common Pitfalls

- **Not monitoring transaction counts until the bill arrives**: Sage does not block requests when you exceed Tier 1 limits -- it silently accrues overage charges. A runaway integration can generate thousands of dollars in unexpected fees. Fix: `Check usage weekly at Company > Admin > Usage Insights > API Usage. Set up alerts at 50% and 80% of monthly allotment.` [src6]
- **Mixing getAPISession with other functions**: The Sage documentation explicitly states to isolate getAPISession in its own request. Combining it with business logic functions causes unpredictable behavior. Fix: `Always send getAPISession as the only function in its request.` [src2]
- **Using DTD 2.1 for new integrations**: DTD 2.1 is legacy and will not receive new features. Fix: `Always use dtdversion 3.0 in the control block for all new development.` [src1]
- **Not handling partial success in multi-function requests**: When `transaction="false"` (default), some functions may succeed while others fail within the same request. Fix: `Check the status of each result element individually. Use transaction="true" when all-or-nothing behavior is required.` [src1]
- **Assuming REST API covers all modules**: Developers start with REST, then discover mid-project that their required module (project accounting, fixed assets, etc.) is XML-only. Fix: `Verify REST API coverage for all required objects before architecture decisions. Check the REST-to-XML object mapping on developer.sage.com.` [src3]
- **Hardcoding the XML gateway URL for all requests**: After getAPISession, the generic gateway still works but routes less efficiently than the session-specific endpoint. Fix: `Always extract and use the endpoint URL from the getAPISession response.` [src2]

## Diagnostic Commands

```bash
# Test XML API authentication -- create session
curl -s -X POST "https://api.intacct.com/ia/xml/xmlgw.phtml" \
  -H "Content-Type: application/xml" \
  -d '<?xml version="1.0" encoding="UTF-8"?>
  <request>
    <control>
      <senderid>YOUR_SENDER_ID</senderid>
      <password>YOUR_SENDER_PASSWORD</password>
      <controlid>diag-session</controlid>
      <uniqueid>false</uniqueid>
      <dtdversion>3.0</dtdversion>
    </control>
    <operation>
      <authentication>
        <login>
          <userid>YOUR_USER_ID</userid>
          <companyid>YOUR_COMPANY_ID</companyid>
          <password>YOUR_PASSWORD</password>
        </login>
      </authentication>
      <content>
        <function controlid="diag-1"><getAPISession/></function>
      </content>
    </operation>
  </request>'
# Expected: <status>success</status> with sessionid and endpoint

# Test REST API authentication -- get token
curl -s -X POST "https://api.intacct.com/ia/api/v1/oauth2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET"
# Expected: JSON with access_token, token_type, expires_in

# Test REST API -- list GL accounts
curl -s "https://api.intacct.com/ia/api/v1/objects/general-ledger/account" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json"
# Expected: JSON array of GL account objects

# Check API usage (must be done via Sage Intacct UI)
# Navigate to: Company > Admin > Usage Insights > API Usage
# Shows: monthly transaction count by sender ID, trending, and remaining allotment

# Verify sender ID authorization (XML API)
# Navigate to: Company > Admin > Web Services Authorizations
# Confirm your sender ID appears in the list of authorized senders
```

## Version History & Compatibility

| API Version | Release Date | Status | Breaking Changes | Migration Notes |
|---|---|---|---|---|
| REST API v1 | 2023 (initial GA) | Current (expanding) | N/A -- additive releases only | New modules added quarterly via Sage releases (R1-R4) [src3] |
| XML DTD 3.0 | 2018+ | Current (maintenance) | None recent | Full feature coverage; recommended for XML integrations [src1] |
| XML DTD 2.1 | Pre-2018 | Legacy (supported) | No new features | Migrate to DTD 3.0; change dtdversion in control block [src1] |
| HTTP GET blocking | 2023-01-15 | Enforced | GET requests to XML gateway rejected | Switch all XML API calls to POST method [src1] |
| REST API 2025 R4 | 2025-12 | Current | New objects added | Check release notes for newly supported REST objects [src3] |

[src1, src3]

### Deprecation Policy

Sage follows a quarterly release cycle (R1-R4) for the REST API with additive-only changes -- existing endpoints are not removed without significant advance notice. The XML API (DTD 3.0) is in long-term maintenance mode and continues to receive bug fixes but no new features. DTD 2.1 remains supported but has no defined end-of-life date; Sage recommends migrating to DTD 3.0. [src1, src3]

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Building new GL, AP, AR, or cash management integrations | Need project accounting, fixed assets, or custom objects | XML API DTD 3.0 for unsupported modules [src3] |
| Mid-market financial management with < 100K API calls/month | High-volume transactional processing (>250K calls/month) without budget for premium tiers | Consider iPaaS with batch optimization or Tier 2+ upgrade [src7] |
| OAuth 2.0 is your standard auth pattern (REST API) | Your platform only supports session-based auth natively | XML API with getAPISession [src1] |
| Need simple JSON payloads for standard financial records | Need complex multi-step transactions with rollback | XML API with transaction="true" [src1] |
| Single integration thread with low concurrency needs | Need >2 concurrent connections without premium tier | Upgrade tier or serialize with queue [src4] |

## Important Caveats

- Sage Intacct's REST API is actively expanding but does not yet cover the full breadth of the XML API. Always verify module coverage before committing to REST-only architecture. New REST objects are added with each quarterly release (R1-R4). [src3]
- Performance Tier 1 (100K transactions/month) is included with all subscriptions, but overage fees are charged retroactively without blocking -- there is no hard stop. Monitor usage proactively. [src6, src7]
- The default concurrency limit of 2 connections per tenant is surprisingly low for organizations running multiple integrations. Upgrading requires contacting your CSM and paying for a higher performance tier. [src4]
- Session-specific endpoints returned by getAPISession should always be validated as `*.intacct.com` domains to prevent session hijacking. Do not blindly trust session IDs received from untrusted sources. [src1]
- There is no free sandbox or developer environment. All testing and development requires a paid Sage Intacct instance, which increases the cost of integration development compared to competitors like NetSuite or Salesforce. [src8]

## Related Units

- [NetSuite REST API capabilities](/business/erp-integration/netsuite-rest-api/2026)
- [AP automation integration playbook](/business/erp-integration/ap-automation-integration-playbook/2026)
- [QuickBooks Online API](/business/erp-integration/quickbooks-online-api/2026)
