Sage Intacct API: REST & XML Web Services Capabilities, Rate Limits, and Session Management
What are the Sage Intacct REST API capabilities, rate limits, and session management patterns?
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).
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.
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.
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).
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.
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)
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 recommended
100K txn/month, 2 concurrent
Yes
Yes (batched)
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
N/A
Platform-dependent
Yes
N/A
CSV Import
File upload
Bulk data loading
Varies by object
N/A
No
Yes
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
Max records per manipulation
100 (recommended)
XML API create/update/delete
Larger batches risk 15-min timeout
Max request processing time
15 minutes
XML API
Gateway returns timeout; backend may still process
Max concurrent connections (default)
2 per tenant
Both APIs
3rd request held ~30s then rejected
Max concurrent connections (per-app)
6 (app) / 8 (company total)
Higher tiers
Contractual; contact CSM to upgrade
Rolling / Daily Limits
Limit Type
Value
Window
Edition Differences
API transactions (Tier 1)
100,000
Monthly
Default for all editions; overage charged, not blocked
API transactions (Tier 2-4)
Up to 2,500,000
Monthly
Premium tiers; contact CSM for pricing
API transactions (Tier 5)
Custom / unlimited
Monthly
Enterprise agreement
Overage fee (Tier 1)
$0.15 per 10-txn pack
Per-month
Charged retroactively
Transaction Counting Rules
Operation
Transaction Count
Notes
Single create/update/delete
1
Each API function call counted individually
Header + line items (e.g., AP bill with 50 lines)
1
Header objects count as 1 regardless of line items
Batch of 100 vendor creates in one request
100
Each function call is a separate transaction
query/readByQuery
1
Each query counts as 1 transaction
getAPISession
1
Session creation is a transaction
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
Client Credentials
Server-to-server, unattended integrations
JWT-based (configurable)
Yes
Recommended for daemon/service integrations
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
Session auth (getAPISession)
All repeated API calls
30 min inactivity timeout
Auto-extends on activity
Preferred; returns session ID + unique endpoint
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. [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. [src1]
Session endpoints differ per session: getAPISession returns a unique endpoint URL. 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. [src1, src3]
Constraints
REST API coverage is incomplete -- project accounting, fixed assets, custom objects, consolidations, and several other modules are only accessible via the XML API.
2 concurrent connections is the default -- exceeding this causes the 3rd request to queue for ~30 seconds, then fail.
100,000 transactions/month on Tier 1 -- 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.
No free sandbox environment -- testing requires a paid Sage Intacct instance.
XML API session timeout of 30 minutes -- long-running batch processes must implement session refresh logic.
HTTP GET requests to XML gateway are blocked as of January 15, 2023.
Line items cannot be updated directly -- must update the entire header to modify line items.
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)
| | |-- YES, but need session-based auth or XML batching
| | --> XML API DTD 3.0
| |-- Module NOT covered by REST? (Project Accounting, Fixed Assets, Custom Objects)
| --> XML API DTD 3.0 (required)
|-- What's the integration pattern?
| |-- Real-time (< 100 records) --> REST API or XML API single function
| |-- Batch (< 100 records) --> XML API with transaction="true"
| |-- Batch (100-1,000 records) --> XML API with chunked batches
| |-- Batch (> 1,000 records) --> XML API chunked + monitor tier usage
|-- Concurrency needs?
| |-- <= 2 concurrent --> Default Tier 1
| |-- > 2 concurrent --> Upgrade performance tier via CSM
|-- Monthly transaction budget?
|-- < 100,000 --> Tier 1 (included)
|-- 100,000-2,500,000 --> Tier 2-4 (paid upgrade)
|-- > 2,500,000 --> Tier 5 (enterprise agreement)
Register as a Sage Intacct developer or obtain a Web Services Developer License. You need: sender ID, sender password, company ID, Web Services user ID, and user password. [src1]
2. Create an API session (XML API)
Use getAPISession to obtain a session ID and unique endpoint. Always isolate getAPISession in its own request. [src1, src2]
Verify: Response contains access_token, 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 exponential backoff. [src5]
Code Examples
Python: Query AP bills via XML API
# 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):
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="q1">
<query>
<object>APBILL</object>
<select>
<field>RECORDNO</field>
<field>VENDORID</field>
<field>TOTALDUE</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)
root = ET.fromstring(resp.text)
records = root.findall(".//data/APBILL")
return [{child.tag: child.text for child in r} for r in records]
JavaScript/Node.js: Create vendor via XML SDK
// Input: Sage Intacct credentials, vendor data
// Output: Created vendor record number
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;
const response = await client.execute(create);
return response.getResults()[0].data;
}
cURL: Quick session creation and query
# Step 1: Create session
curl -s -X POST "https://api.intacct.com/ia/xml/xmlgw.phtml" \
-H "Content-Type: application/xml" \
-d '<request>...getAPISession...</request>'
# Extract sessionid and endpoint from response
# Step 2: REST API -- list GL accounts
curl -s "https://api.intacct.com/ia/api/v1/objects/general-ledger/account" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
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
/objects/general-ledger/journal-entry
GLBATCH
General Ledger
REST uses "journal-entry"; XML uses "GLBATCH"
/objects/accounts-payable/bill
APBILL
Accounts Payable
Full CRUD both APIs
/objects/accounts-payable/vendor
VENDOR
Accounts Payable
Full CRUD both APIs
/objects/accounts-receivable/invoice
ARINVOICE
Accounts Receivable
Full CRUD both APIs
/objects/cash-management/bank-account
BANKACCOUNT
Cash Management
REST coverage available
N/A
PROJECT
Project Accounting
XML API only
N/A
FIXEDASSET
Fixed Assets
XML API only
Data Type Gotchas
Date formats differ: REST uses ISO 8601 (2026-03-02); XML uses MM/DD/YYYY or separate year/month/day elements. [src1, src3]
Boolean fields: XML uses strings "true"/"false" or "T"/"F" depending on the object. REST uses JSON true/false. [src1]
Record numbers vs IDs: Sage Intacct uses both RECORDNO (system-generated integer) and user-defined IDs. XML often requires RECORDNO for updates. [src1]
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
XL03000006
Authentication failure
Invalid sender ID or unauthorized sender
Verify sender ID is authorized in target company
GW-0010
Rate limit / throttling
Too many concurrent requests
Implement exponential backoff; reduce concurrency
GW-0011
Invalid request
Malformed gateway request
Validate XML structure; ensure POST method
BL01001973
Object not found
Referenced record does not exist
Verify record exists before create/update
BL34000061
Session expired
30-minute inactivity timeout
Create new session via getAPISession
429
Too Many Requests
REST API rate limit exceeded
Exponential backoff; respect Retry-After header
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 every 20 minutes, or create a new session before each batch chunk. [src1]
Concurrent connection rejection: With only 2 default concurrent connections, parallel integration processes cause GW-0010 errors. Fix: Serialize API calls with a queue, or upgrade performance tier. [src4]
Transaction count explosion: Nightly syncs creating records individually burn through 100K monthly limit in days. Fix: Batch operations. Monitor at Company > Admin > Usage Insights > API Usage. [src6]
Mid-process failures with no idempotency: 15-minute timeout with partial backend processing creates duplicates on retry. Fix: Set uniqueid=true in control block with consistent controlid values. [src5]
Anti-Patterns
Wrong: Using login authentication for every request
<!-- BAD -- creates a new session for every API call, wasting transactions -->
<authentication>
<login>
<userid>ws_user</userid>
<companyid>MYCOMPANY</companyid>
<password>mypassword</password>
</login>
</authentication>
Correct: Create session once, reuse for all requests
<!-- GOOD -- one getAPISession, then reuse sessionid -->
<authentication>
<sessionid>REUSED_SESSION_ID</sessionid>
</authentication>
Wrong: Sending one function per request for bulk operations
# BAD -- 500 separate HTTP requests for 500 vendor creates
for vendor in vendors:
response = send_xml_request(create_vendor_xml(vendor))
Correct: Batch multiple functions in a single request
# GOOD -- one HTTP request with multiple functions
functions_xml = "".join([create_vendor_xml(v) for v in vendors[:100]])
request_xml = wrap_in_operation(functions_xml, transaction=True)
response = send_xml_request(request_xml)
Wrong: Ignoring the unique session endpoint
# BAD -- always sending to the generic gateway
SESSION_ENDPOINT = "https://api.intacct.com/ia/xml/xmlgw.phtml"
Correct: Use the endpoint returned by getAPISession
# GOOD -- use the session-specific endpoint
session_info = create_session()
SESSION_ENDPOINT = session_info["endpoint"] # e.g., https://na12.intacct.com/...
Common Pitfalls
Not monitoring transaction counts: Sage does not block requests on overage -- it silently accrues charges. Fix: Check usage weekly at Company > Admin > Usage Insights > API Usage. [src6]
Mixing getAPISession with other functions: 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. [src1]
Not handling partial success in multi-function requests: When transaction="false" (default), some functions may succeed while others fail. Fix: Check each result individually. Use transaction="true" for all-or-nothing. [src1]
Assuming REST API covers all modules: Developers discover mid-project that required modules are XML-only. Fix: Verify REST coverage for all objects before architecture decisions. [src3]
Hardcoding the XML gateway URL: The generic gateway works but routes less efficiently. Fix: Use the endpoint URL from getAPISession response. [src2]
Diagnostic Commands
# Test XML API authentication -- create session
curl -s -X POST "https://api.intacct.com/ia/xml/xmlgw.phtml" \
-H "Content-Type: application/xml" \
-d '<request><control>...</control><operation>...getAPISession...</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_ID&client_secret=YOUR_SECRET"
# Test REST API -- list GL accounts
curl -s "https://api.intacct.com/ia/api/v1/objects/general-ledger/account" \
-H "Authorization: Bearer YOUR_TOKEN"
# Check API usage: Company > Admin > Usage Insights > API Usage (UI only)
Version History & Compatibility
API Version
Release Date
Status
Breaking Changes
Migration Notes
REST API v1
2023
Current (expanding)
N/A -- additive only
New modules added quarterly (R1-R4)
XML DTD 3.0
2018+
Current (maintenance)
None recent
Full feature coverage; recommended for XML
XML DTD 2.1
Pre-2018
Legacy (supported)
No new features
Migrate to DTD 3.0
HTTP GET blocking
2023-01-15
Enforced
GET requests rejected
Use POST for all XML API calls
REST API 2025 R4
2025-12
Current
New objects added
Check release notes
When to Use / When Not to Use
Use When
Don't Use When
Use Instead
New GL, AP, AR, or cash management integrations
Need project accounting, fixed assets, or custom objects
XML API DTD 3.0 for unsupported modules
Mid-market financial management with <100K API calls/month
High-volume processing (>250K calls/month) without budget for premium tiers
iPaaS with batch optimization or Tier 2+ upgrade
OAuth 2.0 is your standard auth pattern (REST API)
Platform only supports session-based auth natively
XML API with getAPISession
Simple JSON payloads for standard financial records
Complex multi-step transactions with rollback
XML API with transaction="true"
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.
Performance Tier 1 (100K transactions/month) is included with all subscriptions, but overage fees are charged retroactively without blocking.
The default concurrency limit of 2 connections per tenant is surprisingly low for organizations running multiple integrations.
Session-specific endpoints should always be validated as *.intacct.com domains to prevent session hijacking.
There is no free sandbox or developer environment. All testing requires a paid Sage Intacct instance.