Salesforce Composite API Capabilities & Subrequest Limits

Type: ERP Integration System: Salesforce (API v66.0) Confidence: 0.92 Sources: 8 Verified: 2026-03-01 Freshness: 2026-03-01

TL;DR

System Profile

This card covers all four Salesforce REST API composite resource types: Composite, Composite Graph, Composite Batch, and sObject Collections/Tree. These resources allow combining multiple REST API operations into a single HTTP call, reducing round trips and API call consumption. Available in API v43.0+ for sObject Collections and v50.0+ for Composite Graph. All editions that support REST API support composite resources, but API call daily limits vary significantly by edition.

PropertyValue
VendorSalesforce
SystemSalesforce Platform (Spring '26)
API SurfaceREST — Composite Resources
Current API Versionv66.0 (Spring '26)
Editions CoveredEnterprise, Unlimited, Developer, Performance
DeploymentCloud
API DocsREST API Composite Resources
StatusGA (all four resource types)

API Surfaces & Capabilities

Salesforce provides four distinct composite resource types, each optimized for different integration patterns. The key differentiator is whether subrequests can reference each other's outputs and whether failures trigger full rollback.

API SurfaceProtocolBest ForMax SubrequestsReference IDs?All-or-None?Transactional?
CompositeHTTPS/JSONMulti-step workflows needing cross-reference25 (max 5 queries/collections)YesOptional (allOrNone param)Yes, when allOrNone=true
Composite GraphHTTPS/JSONLarge-scale related record creation; complex dependency graphs500 per graphYes (per graph only)Implicit per graphYes, per graph
Composite BatchHTTPS/JSONIndependent parallel operations (no dependencies)25NoNo — each runs independentlyNo
sObject CollectionsHTTPS/JSONBulk DML on single object type200 recordsN/AOptional (allOrNone param)Yes, when allOrNone=true
sObject TreeHTTPS/JSONParent-child record hierarchies200 records totalN/A (implicit parent-child)Always all-or-nothingYes

Rate Limits & Quotas

Per-Request Limits

Limit TypeValueApplies ToNotes
Max subrequests (Composite)25Composite APIMax 5 can be query or sObject Collections operations
Max subrequests (Composite Graph)500 per graphComposite GraphCan have multiple graphs; 500 total across all graphs
Max subrequests (Composite Batch)25Composite BatchEach subrequest executes independently
Max records (sObject Collections)200Collections create/update/delete/upsertPer single Collections call
Max records (sObject Tree)200 totalsObject TreeMax 5 levels deep; max 5 sObject types
Max query result size2,000 recordsSOQL via CompositeUse queryMore for pagination
Max request body size50 MBAll REST APIApplies to entire composite payload

Rolling / Daily Limits

Limit TypeValueWindowEdition Differences
API calls100,000 base24h rollingEnterprise: 100K + (users x 1,000); Unlimited: 5M; Developer: 15K
Concurrent long-running requests25Per orgApplies to requests >20s
Composite call consumption1 API call per composite requestPer requestAll subrequests count as 1 call against daily limit

Transaction / Governor Limits

Governor limits apply cumulatively across all subrequests within a single composite call. This is the most common source of composite API failures.

Limit TypePer-Transaction ValueNotes
SOQL queries100 (sync) / 200 (async)Shared across all subrequests — triggers consume from same pool
DML statements150Each insert/update/delete counts as 1
Records retrieved by SOQL50,000Total across all queries in the transaction
Records processed by DML10,000Total across all DML operations
CPU time10,000 ms (sync) / 60,000 ms (async)Exceeded = entire transaction aborted
Heap size6 MB (sync) / 12 MB (async)Monitor when handling large payloads
Callouts100If subrequests trigger Apex with callouts

Authentication

All composite resources use the same authentication as standard Salesforce REST API. No special scopes or permissions required beyond standard API access.

FlowUse WhenToken LifetimeRefresh?Notes
OAuth 2.0 JWT BearerServer-to-server, no user contextSession timeout (default 2h)New JWT per requestRecommended for integrations
OAuth 2.0 Web ServerUser-context operationsAccess: 2h, Refresh: until revokedYesRequires callback URL
OAuth 2.0 Client CredentialsServer-to-server (Spring '23+)Access: 2hNo, request new tokenSimpler than JWT

Authentication Gotchas

Constraints

Integration Pattern Decision Tree

START — Need to combine multiple Salesforce REST operations into one call
├── Do subrequests need to reference each other's output?
│   ├── YES — need reference IDs
│   │   ├── Need ≤25 subrequests? → Composite API (allOrNone=true for atomicity)
│   │   └── Need >25 subrequests? → Composite Graph API (up to 500 per graph)
│   └── NO — subrequests are independent
│       ├── All DML on same object type? → sObject Collections (200 max)
│       ├── Creating parent-child hierarchies? → sObject Tree (200 max)
│       └── Mixed independent operations? → Composite Batch (25 max)
├── Volume check
│   ├── ≤200 records of same type → sObject Collections
│   ├── ≤200 records with parent-child → sObject Tree
│   ├── ≤25 mixed operations with refs → Composite API
│   ├── ≤500 operations with refs → Composite Graph
│   └── >500 records → Bulk API 2.0
└── Error handling preference
    ├── All-or-nothing required → Composite (allOrNone=true), Graph, or Tree
    ├── Partial success OK → Batch or Collections (allOrNone=false)
    └── Per-group atomicity → Composite Graph (each graph is atomic)

Quick Reference

OperationResourceMethodEndpointMax RecordsRef IDs?Atomicity
Multi-step workflowCompositePOST/services/data/v66.0/composite25 subrequestsYesOptional
Large dependency graphComposite GraphPOST/services/data/v66.0/composite/graph500/graphYes (per graph)Per graph
Independent parallel opsComposite BatchPOST/services/data/v66.0/composite/batch25 subrequestsNoNone
Bulk create (single type)sObject CollectionsPOST/services/data/v66.0/composite/sobjects200 recordsN/AOptional
Bulk update (single type)sObject CollectionsPATCH/services/data/v66.0/composite/sobjects200 recordsN/AOptional
Bulk delete (single type)sObject CollectionsDELETE/services/data/v66.0/composite/sobjects?ids=...200 recordsN/AOptional
Parent-child treesObject TreePOST/services/data/v66.0/composite/tree/{sObject}200 totalN/AAlways

Step-by-Step Integration Guide

1. Authenticate and obtain access token

Use OAuth 2.0 JWT bearer flow for server-to-server integrations. [src1]

# Input:  Connected App consumer key, private key, username
# Output: Access token and instance URL

curl -X POST https://login.salesforce.com/services/oauth2/token \
  -d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
  -d "assertion=$(generate_jwt_assertion)"

Verify: Response contains access_token and instance_url fields. HTTP 200 = success.

2. Execute a Composite request with reference IDs

Create an Account and linked Contact in a single atomic call. [src1, src4]

curl -X POST https://{instance_url}/services/data/v66.0/composite \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "allOrNone": true,
    "collateSubrequests": true,
    "compositeRequest": [
      {
        "method": "POST",
        "url": "/services/data/v66.0/sobjects/Account",
        "referenceId": "refAccount",
        "body": {"Name": "Acme Corp", "Industry": "Technology"}
      },
      {
        "method": "POST",
        "url": "/services/data/v66.0/sobjects/Contact",
        "referenceId": "refContact",
        "body": {
          "FirstName": "Jane", "LastName": "Doe",
          "AccountId": "@{refAccount.id}"
        }
      }
    ]
  }'

Verify: Response HTTP 200 with compositeResponse array. Each element has httpStatusCode: 200.

3. Execute a Composite Graph request

Use Composite Graph for >25 subrequests or multiple independent transactional groups. [src2, src3]

curl -X POST https://{instance_url}/services/data/v66.0/composite/graph \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "graphs": [
      {
        "graphId": "graph1",
        "compositeRequest": [
          {"method": "POST", "url": "/services/data/v66.0/sobjects/Account",
           "referenceId": "ref1Acct", "body": {"Name": "Graph1 Corp"}},
          {"method": "POST", "url": "/services/data/v66.0/sobjects/Contact",
           "referenceId": "ref1Cont", "body": {"LastName": "Smith", "AccountId": "@{ref1Acct.id}"}}
        ]
      },
      {
        "graphId": "graph2",
        "compositeRequest": [
          {"method": "POST", "url": "/services/data/v66.0/sobjects/Account",
           "referenceId": "ref2Acct", "body": {"Name": "Graph2 Inc"}}
        ]
      }
    ]
  }'

Verify: Response graphs array. Each graph has isSuccessful: true/false. If graph1 fails, graph2 can still succeed.

4. Use sObject Collections for bulk DML

Batch create up to 200 records of the same sObject type. [src5]

curl -X POST https://{instance_url}/services/data/v66.0/composite/sobjects \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "allOrNone": false,
    "records": [
      {"attributes": {"type": "Contact"}, "FirstName": "Alice", "LastName": "Johnson"},
      {"attributes": {"type": "Contact"}, "FirstName": "Bob", "LastName": "Williams"}
    ]
  }'

Verify: Response is array of SaveResult: [{"id": "003xx...", "success": true, "errors": []}, ...]

Code Examples

Python: Composite request with reference IDs

# Input:  Salesforce access token, instance URL
# Output: Created Account ID and linked Contact ID

import requests  # requests>=2.31.0

def composite_create(instance_url, access_token):
    """Create Account + Contact atomically via Composite API."""
    url = f"{instance_url}/services/data/v66.0/composite"
    headers = {
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json"
    }
    payload = {
        "allOrNone": True,
        "collateSubrequests": True,
        "compositeRequest": [
            {
                "method": "POST",
                "url": "/services/data/v66.0/sobjects/Account",
                "referenceId": "refAccount",
                "body": {"Name": "Acme Corp"}
            },
            {
                "method": "POST",
                "url": "/services/data/v66.0/sobjects/Contact",
                "referenceId": "refContact",
                "body": {
                    "LastName": "Doe",
                    "AccountId": "@{refAccount.id}"
                }
            }
        ]
    }
    resp = requests.post(url, json=payload, headers=headers)
    resp.raise_for_status()
    results = resp.json()["compositeResponse"]
    return {r["referenceId"]: r["body"]["id"] for r in results}

JavaScript/Node.js: sObject Collections bulk upsert

// Input:  jsforce connection, array of records, external ID field
// Output: Array of SaveResult objects

const jsforce = require("jsforce"); // [email protected]

async function bulkCollectionsUpsert(conn, records, externalIdField) {
  const CHUNK_SIZE = 200; // sObject Collections max
  const results = [];
  for (let i = 0; i < records.length; i += CHUNK_SIZE) {
    const chunk = records.slice(i, i + CHUNK_SIZE);
    const res = await conn.requestPost(
      `/services/data/v66.0/composite/sobjects/${externalIdField}`,
      { allOrNone: false, records: chunk }
    );
    results.push(...res);
  }
  return results; // [{id, success, errors, created}, ...]
}

cURL: Composite Batch for independent queries

# Input:  Access token, instance URL
# Output: Array of independent results

curl -X POST https://{instance_url}/services/data/v66.0/composite/batch \
  -H "Authorization: Bearer {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "batchRequests": [
      {"method": "GET", "url": "v66.0/sobjects/Account/describe"},
      {"method": "GET", "url": "v66.0/query?q=SELECT+Id,Name+FROM+Account+LIMIT+5"}
    ]
  }'
# Response: {"hasErrors": false, "results": [{statusCode: 200, result: {...}}, ...]}

Data Mapping

Reference ID Syntax

PatternPurposeExampleNotes
@{referenceId.id}Use created record's ID in later subrequest"AccountId": "@{refAccount.id}"Most common — link child to parent
@{referenceId.field}Access any field from response body@{refQuery.records[0].Id}Works with query results
referenceId in URLDynamic URL construction/sobjects/Account/@{refAccount.id}For PATCH/DELETE after create

Data Type Gotchas

Error Handling & Failure Points

Common Error Codes

CodeMeaningCauseResolution
400Bad RequestMalformed JSON, invalid reference IDValidate payload structure; check referenceId names
401UnauthorizedExpired or invalid access tokenRefresh token or re-authenticate
PROCESSING_HALTEDSubrequest skippedallOrNone=true and earlier subrequest failedFix the failing subrequest
INVALID_FIELDField doesn't exist or isn't writableWrong API name or insufficient FLSCheck via /sobjects/{Object}/describe
UNABLE_TO_LOCK_ROWRecord lockedConcurrent updates to same recordRetry with random jitter (100-500ms)
LIMIT_EXCEEDEDGovernor limit breachedCumulative SOQL/DML/CPU exceededReduce subrequest count or split requests
DUPLICATE_VALUEUnique constraint violatedDuplicate external IDUse upsert; implement idempotency

Failure Points in Production

Anti-Patterns

Wrong: Making 25 individual API calls instead of 1 composite call

# BAD — 25 API calls consumed, 25 round trips
for record in records[:25]:
    requests.post(f"{url}/sobjects/Contact", json=record, headers=headers)

Correct: Bundle into a single Composite or Collections call

# GOOD — 1 API call consumed, 1 round trip
payload = {
    "allOrNone": False,
    "records": [{"attributes": {"type": "Contact"}, **r} for r in records[:200]]
}
requests.post(f"{url}/composite/sobjects", json=payload, headers=headers)

Wrong: Using Composite Batch when you need atomicity

# BAD — Composite Batch has no allOrNone or reference IDs
# If subrequest 2 fails, subrequest 1 is already committed
payload = {"batchRequests": [
    {"method": "POST", "url": "v66.0/sobjects/Account", "richInput": {"Name": "Acme"}},
    {"method": "POST", "url": "v66.0/sobjects/Contact", "richInput": {"LastName": "Doe"}}
]}

Correct: Use Composite API with allOrNone for transactional workflows

# GOOD — allOrNone=true ensures both succeed or both roll back
payload = {
    "allOrNone": True,
    "compositeRequest": [
        {"method": "POST", "url": "/services/data/v66.0/sobjects/Account",
         "referenceId": "refAcct", "body": {"Name": "Acme"}},
        {"method": "POST", "url": "/services/data/v66.0/sobjects/Contact",
         "referenceId": "refCont", "body": {"LastName": "Doe", "AccountId": "@{refAcct.id}"}}
    ]
}

Wrong: Putting >5 queries inside Composite API

# BAD — Composite allows max 5 query/collection subrequests
payload = {"compositeRequest": [
    {"method": "GET", "url": f"/services/data/v66.0/query?q=SELECT...", "referenceId": f"q{i}"}
    for i in range(10)  # 10 queries exceeds the 5-query cap
]}

Correct: Use Composite Batch for independent queries

# GOOD — Composite Batch has no query-count restriction within 25 subrequests
payload = {"batchRequests": [
    {"method": "GET", "url": f"v66.0/query?q=SELECT...{table}"}
    for table in tables[:25]
]}

Common Pitfalls

Diagnostic Commands

# Check remaining API limits for this org
curl -H "Authorization: Bearer {token}" \
  https://{instance_url}/services/data/v66.0/limits

# Verify composite resource availability
curl -H "Authorization: Bearer {token}" \
  https://{instance_url}/services/data/v66.0/

# Describe an sObject to check field API names
curl -H "Authorization: Bearer {token}" \
  https://{instance_url}/services/data/v66.0/sobjects/Account/describe

# Test a minimal composite batch request
curl -X POST -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  https://{instance_url}/services/data/v66.0/composite/batch \
  -d '{"batchRequests":[{"method":"GET","url":"v66.0/limits"}]}'

# Check available API versions
curl https://{instance_url}/services/data/

Version History & Compatibility

API VersionReleaseStatusKey Composite FeaturesNotes
v66.0Spring '26 (Feb 2026)CurrentNo new composite featuresLegacy hostname redirects removed
v63.0Spring '25 (Feb 2025)SupportedNone
v50.0Winter '21 (Oct 2020)SupportedComposite Graph API introduced500 subrequests per graph
v43.0Summer '18 (Jun 2018)SupportedsObject Collections introduced200-record DML
v38.0Winter '17 (Oct 2016)SupportedComposite API introduced25 subrequests with reference IDs
v34.0Summer '15 (Jun 2015)SupportedComposite Batch introduced25 independent subrequests
v30.0 and belowPre-2014Retired (Jun 2025)N/AMinimum version: v31.0

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Creating related records atomically (parent + children)Bulk data migration (>1,000 records)Bulk API 2.0
Need to reference output of one operation in a subsequent operationOperations are completely independentComposite Batch
Reducing API call consumption (25 ops = 1 API call)Processing >500 related recordsBulk API 2.0 with external IDs
Building multi-step workflows (query, transform, upsert)Simple single-object CRUD on one recordStandard REST API
Creating up to 200 records of same type efficientlyNeed real-time event notificationsPlatform Events / CDC

Important Caveats

Related Units