Sage Intacct API: REST & XML Web Services Capabilities, Rate Limits, and Session Management
Type: ERP Integration
System: Sage Intacct (REST API v1 / XML DTD 3.0)
Confidence: 0.88
Sources: 8
Verified: 2026-03-02
Freshness: 2026-03-02
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.
| 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 | REST API / XML 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) | 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)
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 |
| Query records (XML) | POST | {session_endpoint} | XML (query) | Max 1,000 recommended; paginate with resultId |
| Create record (XML) | POST | {session_endpoint} | XML (create) | Each function = 1 transaction |
| OAuth token (REST) | POST | https://api.intacct.com/ia/api/v1/oauth2/token | JSON | Returns JWT bearer token |
| List objects (REST) | GET | https://api.intacct.com/ia/api/v1/objects/{module}/{object} | N/A | Bearer token in Authorization header |
| Create record (REST) | POST | https://api.intacct.com/ia/api/v1/objects/{module}/{object} | JSON | Bearer token; Content-Type: application/json |
| Get bank account (REST) | GET | https://api.intacct.com/ia/api/v1/objects/cash-management/bank-account/{key} | N/A | Bearer token |
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. 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]
<?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.
3. Make API calls using session (XML API)
Replace login authentication with the session ID. Send all requests to the unique endpoint returned by getAPISession. [src1]
4. Handle pagination (XML API)
Use readMore with the resultId from previous response to fetch the next page. [src1]
5. Authenticate with OAuth 2.0 (REST API)
Obtain an OAuth 2.0 bearer token from the token endpoint for REST API access. [src3]
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, 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.
Related Units