---
# === IDENTITY ===
id: business/erp-integration/salesforce-composite-api-capabilities/2026
canonical_question: "What are the Salesforce Composite API and Composite Graph API subrequest limits and capabilities?"
aliases:
  - "Salesforce composite request subrequest limit 25"
  - "Composite Graph API 500 subrequests all-or-nothing"
  - "sObject Collections vs Composite Batch vs sObject Tree API"
  - "Salesforce composite API reference ID between subrequests"
entity_type: erp_integration
domain: business > erp-integration > salesforce-composite-api-capabilities
region: global
jurisdiction: global
temporal_scope: 2025-2026

# === SYSTEM PROFILE ===
systems:
  - name: "Salesforce"
    vendor: "Salesforce"
    version: "API v66.0 (Spring '26)"
    edition: "Enterprise, Unlimited, Developer, Performance"
    deployment: cloud
    api_surface: "REST — Composite Resources"

# === VERIFICATION ===
last_verified: 2026-03-01
confidence: 0.92
version: 1.0
first_published: 2026-03-01

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: volatile
  last_breaking_change: "v66.0 (Spring '26) — legacy hostname redirects removed; Connected App creation disabled by default"
  next_review: 2026-08-28
  change_sensitivity: high

# === CONSTRAINTS ===
constraints:
  - "Composite API: max 25 subrequests per call; max 5 can be query or sObject Collections operations"
  - "Composite Graph: max 500 subrequests per graph; max 500 total across all graphs in a single request"
  - "sObject Collections: max 200 records per request (create, update, delete, upsert)"
  - "sObject Tree: max 200 total records across all trees; max 5 levels deep; max 5 sObject types per request"
  - "Composite Batch: max 25 subrequests; no reference IDs between subrequests (independent execution)"
  - "Entire composite request counts as 1 API call against org daily limit, but governor limits (SOQL, DML, CPU) apply cumulatively across all subrequests"
  - "API versions 21.0-30.0 retired June 2025 — minimum supported version is v31.0"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "User needs to process >10,000 records in batch"
    use_instead: "business/erp-integration/salesforce-bulk-api-capabilities/2026"
  - condition: "User needs to understand per-transaction governor limits in depth"
    use_instead: "business/erp-integration/salesforce-governor-limits/2026"
  - condition: "User needs API versioning and deprecation strategy"
    use_instead: "business/erp-integration/salesforce-api-versioning-deprecation/2026"

# === 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 this ERP)"
      - "outbound (reading from this ERP)"
      - "bidirectional sync"

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/business/erp-integration/salesforce-composite-api-capabilities/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-03-01)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to:
    - id: "business/erp-integration/salesforce-bulk-api-capabilities/2026"
      label: "Salesforce Bulk API 2.0 — for >2,000 records"
    - id: "business/erp-integration/salesforce-governor-limits/2026"
      label: "Salesforce Governor Limits — per-transaction constraints"
  solves: []
  alternative_to:
    - id: "business/erp-integration/salesforce-bulk-api-capabilities/2026"
      label: "Bulk API 2.0 — alternative for high-volume operations"
  often_confused_with:
    - id: "business/erp-integration/salesforce-bulk-api-capabilities/2026"
      label: "Bulk API is async/CSV-based; Composite is sync/JSON-based"

# === SOURCES ===
sources:
  - id: src1
    title: "Composite Request Body — REST API Developer Guide"
    author: Salesforce
    url: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/requests_composite.htm
    type: official_docs
    published: 2026-02-01
    reliability: authoritative
  - id: src2
    title: "Composite Graph Limits — REST API Developer Guide"
    author: Salesforce
    url: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_graph_limits.htm
    type: official_docs
    published: 2026-02-01
    reliability: authoritative
  - id: src3
    title: "Using Composite Graphs — REST API Developer Guide"
    author: Salesforce
    url: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_graph_introduction.htm
    type: official_docs
    published: 2026-02-01
    reliability: authoritative
  - id: src4
    title: "allOrNone Parameters in Composite and Collections — REST API Developer Guide"
    author: Salesforce
    url: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_allornone.htm
    type: official_docs
    published: 2026-02-01
    reliability: authoritative
  - id: src5
    title: "API Request Limits and Allocations — Salesforce Developer Limits Quick Reference"
    author: Salesforce
    url: https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_api.htm
    type: official_docs
    published: 2026-02-01
    reliability: authoritative
  - id: src6
    title: "Understanding Salesforce Composite API: A Complete Guide with Examples"
    author: The Salesforce Dev
    url: https://thesalesforcedev.in/2025/10/26/understanding-salesforce-composite-api-a-complete-guide-with-examples/
    type: technical_blog
    published: 2025-10-26
    reliability: moderate_high
  - id: src7
    title: "Processing Large Amounts of Data with APIs (Part 2 of 2)"
    author: Salesforce
    url: https://developer.salesforce.com/blogs/2022/12/processing-large-amounts-of-data-with-apis-part-2-of-2
    type: technical_blog
    published: 2022-12-01
    reliability: high
  - id: src8
    title: "Salesforce Spring '26 Release API Updates — API Version 66.0"
    author: Conemis
    url: https://www.conemis.com/news/salesforce-spring-26-release-api-version-66-0
    type: technical_blog
    published: 2026-01-15
    reliability: moderate_high
---

# Salesforce Composite API Capabilities & Subrequest Limits

## TL;DR

- **Bottom line**: Salesforce offers four composite resource types — Composite (25 subrequests with reference chaining), Composite Graph (500 subrequests with per-graph transactions), Composite Batch (25 independent subrequests), and sObject Collections (200 records per DML). Choose based on whether you need cross-subrequest references, all-or-nothing behavior, or maximum throughput. [src1, src2]
- **Key limit**: Composite API caps at 25 subrequests (max 5 queries/collections); Composite Graph scales to 500 but each graph is its own transaction. The entire composite call counts as 1 API call against daily limits. [src1, src2, src5]
- **Watch out for**: Governor limits (100 SOQL queries, 150 DML statements, 10,000ms CPU) apply cumulatively across ALL subrequests in a single composite call — exceeding any governor limit aborts the entire request. [src5, src6]
- **Best for**: Multi-step create/update workflows requiring atomicity (e.g., create Account + Contact + Opportunity in one call), or reducing API call consumption when creating related records. [src1, src7]
- **Authentication**: OAuth 2.0 JWT bearer flow for server-to-server; web server flow for user-context. All composite resources require the same auth as standard REST API. [src1]

## 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. [src1, src5]

| Property | Value |
|---|---|
| **Vendor** | Salesforce |
| **System** | Salesforce Platform (Spring '26) |
| **API Surface** | REST — Composite Resources |
| **Current API Version** | v66.0 (Spring '26) |
| **Editions Covered** | Enterprise, Unlimited, Developer, Performance |
| **Deployment** | Cloud |
| **API Docs URL** | [REST API Composite Resources](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/requests_composite.htm) |
| **Status** | GA (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. [src1, src2, src7]

| API Surface | Protocol | Best For | Max Subrequests | Reference IDs? | All-or-None? | Transactional? |
|---|---|---|---|---|---|---|
| Composite | HTTPS/JSON | Multi-step workflows needing cross-reference (create parent, then child) | 25 (max 5 queries/collections) | Yes — `@{refId.field}` | Optional (`allOrNone` param) | Yes, when `allOrNone=true` |
| Composite Graph | HTTPS/JSON | Large-scale related record creation; complex dependency graphs | 500 per graph | Yes — within same graph only | Implicit per graph | Yes, per graph (not across graphs) |
| Composite Batch | HTTPS/JSON | Independent parallel operations (no dependencies between requests) | 25 | No | No — each runs independently | No — partial success allowed |
| sObject Collections | HTTPS/JSON | Bulk DML on a single object type (create/update/delete/upsert) | 200 records | N/A (single operation) | Optional (`allOrNone` param) | Yes, when `allOrNone=true` |
| sObject Tree | HTTPS/JSON | Parent-child record hierarchies in a single call | 200 records total | N/A (implicit parent-child) | All-or-nothing always | Yes |

[src1, src2, src4, src6]

## Rate Limits & Quotas

### Per-Request Limits

| Limit Type | Value | Applies To | Notes |
|---|---|---|---|
| Max subrequests (Composite) | 25 | Composite API | Max 5 can be query or sObject Collections operations [src1] |
| Max subrequests (Composite Graph) | 500 per graph | Composite Graph | Can have multiple graphs per request; 500 total across all graphs [src2] |
| Max subrequests (Composite Batch) | 25 | Composite Batch | Each subrequest executes independently [src1] |
| Max records (sObject Collections) | 200 | Collections create/update/delete/upsert | Per single Collections call [src5] |
| Max records (sObject Tree) | 200 total | sObject Tree | Across all trees in request; max 5 levels deep; max 5 sObject types [src6] |
| Max query result size | 2,000 records | SOQL via Composite | Use `queryMore` for pagination [src5] |
| Max request body size | 50 MB | All REST API | Applies to entire composite payload [src5] |

### Rolling / Daily Limits

| Limit Type | Value | Window | Edition Differences |
|---|---|---|---|
| API calls | 100,000 base | 24h rolling | Enterprise: 100K + (user count x 1,000); Unlimited: 5M; Developer: 15K [src5] |
| Concurrent API requests | 25 long-running | Per org | Applies to requests >20s [src5] |
| Composite call consumption | 1 API call per composite request | Per request | The 25 subrequests inside count as 1 call against daily limit [src1, src6] |

### Transaction / Governor Limits

Governor limits apply cumulatively across all subrequests within a single composite call. This is the most common source of failures in composite API usage. [src5, src6]

| Limit Type | Per-Transaction Value | Notes |
|---|---|---|
| SOQL queries | 100 (sync) / 200 (async) | Shared across all subrequests — triggers in subrequest 1 consume from same pool as subrequest 25 [src5] |
| DML statements | 150 | Each insert/update/delete/upsert counts as 1 [src5] |
| Records retrieved by SOQL | 50,000 | Total across all queries in the transaction [src5] |
| Records processed by DML | 10,000 | Total across all DML operations [src5] |
| CPU time | 10,000 ms (sync) / 60,000 ms (async) | Exceeded = entire transaction aborted [src5] |
| Heap size | 6 MB (sync) / 12 MB (async) | Monitor when handling large payloads [src5] |
| Callouts (HTTP/web service) | 100 | If subrequests trigger Apex with callouts [src5] |

## Authentication

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

| Flow | Use When | Token Lifetime | Refresh? | Notes |
|---|---|---|---|---|
| OAuth 2.0 JWT Bearer | Server-to-server integrations, no user context | Session timeout (default 2h) | New JWT per request | Recommended for automated integrations [src1] |
| OAuth 2.0 Web Server | User-context operations, interactive apps | Access: 2h, Refresh: until revoked | Yes | Requires callback URL configuration [src1] |
| OAuth 2.0 Client Credentials | Server-to-server (Spring '23+) | Access: 2h | No refresh, request new token | Simpler than JWT but less flexible [src1] |

### Authentication Gotchas

- Connected App creation is disabled by default in Spring '26 (v66.0). Admins must explicitly enable it or migrate to External Client App feature. [src8]
- All subrequests execute in the context of the same user session — permission checks apply per-subrequest but the token is shared. [src1, src6]
- Legacy hostname redirects were removed in Spring '26 — use My Domain URLs exclusively. [src8]

## Constraints
<!-- Agents: read this section before recommending any integration approach from this unit.
     These are hard constraints that must not be violated. -->

- **25-subrequest ceiling on Composite API** — cannot be increased. If you need more, use Composite Graph (500) or split into multiple requests. [src1]
- **5 query/collection cap within Composite** — only 5 of the 25 subrequests can be queries or sObject Collections operations. The rest must be individual record operations. [src1]
- **Governor limits are cumulative** — all subrequests share one governor context. 25 subrequests each doing 5 SOQL queries = 125 total, exceeding the 100-query synchronous limit. [src5, src6]
- **Composite Graph graphs are independent** — reference IDs cannot cross graph boundaries. Each graph is its own transaction. [src2, src3]
- **sObject Collections limited to 200 records** — for volumes above 200, use multiple Collections calls inside a Composite request (counts as 1 API call) or switch to Bulk API 2.0. [src5]
- **sObject Tree is always all-or-nothing** — there is no partial success mode. If any record in the tree fails validation, the entire tree is rolled back. [src6]
- **API versions 21.0-30.0 retired** — minimum supported version is v31.0 as of June 2025. Pin integrations to a supported version. [src8]

## 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 (e.g., create Account, use its ID for Contact)
│   │   ├── Need ≤25 subrequests?
│   │   │   ├── YES → Composite API (allOrNone=true for atomicity)
│   │   │   └── NO → Composite Graph API (up to 500 per graph)
│   │   └── Need atomicity across independent record groups?
│   │       ├── YES (single group) → One Composite Graph graph
│   │       └── YES (multiple independent groups) → Multiple graphs in one Composite Graph request
│   └── NO — subrequests are independent
│       ├── All operations are DML on same object type?
│       │   ├── YES, ≤200 records → sObject Collections
│       │   └── NO, or mixed object types → Composite Batch
│       └── Creating parent-child hierarchies?
│           ├── YES, ≤200 records total → sObject Tree
│           └── NO → 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 (different card)
└── Error handling preference
    ├── All-or-nothing required → Composite (allOrNone=true), Composite Graph, or sObject Tree
    ├── Partial success OK → Composite Batch or sObject Collections (allOrNone=false)
    └── Need per-group atomicity → Composite Graph (each graph is atomic)
```

## Quick Reference

| Operation | Resource | Method | Endpoint | Max Records | Ref IDs? | Atomicity |
|---|---|---|---|---|---|---|
| Multi-step workflow | Composite | POST | `/services/data/v66.0/composite` | 25 subrequests | Yes | Optional |
| Large dependency graph | Composite Graph | POST | `/services/data/v66.0/composite/graph` | 500/graph | Yes (per graph) | Per graph |
| Independent parallel ops | Composite Batch | POST | `/services/data/v66.0/composite/batch` | 25 subrequests | No | None |
| Bulk create (single type) | sObject Collections | POST | `/services/data/v66.0/composite/sobjects` | 200 records | N/A | Optional |
| Bulk update (single type) | sObject Collections | PATCH | `/services/data/v66.0/composite/sobjects` | 200 records | N/A | Optional |
| Bulk delete (single type) | sObject Collections | DELETE | `/services/data/v66.0/composite/sobjects?ids=...` | 200 records | N/A | Optional |
| Bulk upsert (single type) | sObject Collections | PATCH | `/services/data/v66.0/composite/sobjects/{extIdField}` | 200 records | N/A | Optional |
| Parent-child tree | sObject Tree | POST | `/services/data/v66.0/composite/tree/{sObject}` | 200 total | N/A | Always |

[src1, src2, src4, src5]

## Step-by-Step Integration Guide

### 1. Authenticate and obtain access token

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

```bash
# Input:  Connected App consumer key, private key file, 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**: Check response contains `access_token` and `instance_url` fields. HTTP 200 = success.

### 2. Execute a Composite request with reference IDs

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

```bash
# Input:  Access token, Account + Contact data
# Output: IDs of created records or rollback on failure

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` and a `body.id` value.

### 3. Execute a Composite Graph request for large-scale creation

Use Composite Graph when you need more than 25 subrequests or multiple independent transactional groups. [src2, src3]

```bash
# Input:  Access token, multiple record groups
# Output: Per-graph success/failure results

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": "ref1Account",
            "body": { "Name": "Graph1 Corp" }
          },
          {
            "method": "POST",
            "url": "/services/data/v66.0/sobjects/Contact",
            "referenceId": "ref1Contact",
            "body": {
              "LastName": "Smith",
              "AccountId": "@{ref1Account.id}"
            }
          }
        ]
      },
      {
        "graphId": "graph2",
        "compositeRequest": [
          {
            "method": "POST",
            "url": "/services/data/v66.0/sobjects/Account",
            "referenceId": "ref2Account",
            "body": { "Name": "Graph2 Inc" }
          }
        ]
      }
    ]
  }'
```

**Verify**: Response contains `graphs` array. Each graph has `isSuccessful: true/false`. If graph1 fails, graph2 can still succeed (independent transactions). [src3]

### 4. Use sObject Collections for bulk DML on a single type

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

```bash
# Input:  Access token, array of up to 200 records
# Output: Array of SaveResult objects (id, success, errors)

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 `[{"id": "003xx...", "success": true, "errors": []}, ...]`. Check each element's `success` field when `allOrNone=false`.

## Code Examples

### Python: Composite request with reference IDs

```python
# 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

```javascript
// Input:  Salesforce connection (jsforce>=3.0.0), array of records
// Output: Array of SaveResult objects

const jsforce = require("jsforce"); // jsforce@3.4.0

async function bulkCollectionsUpsert(conn, records, externalIdField) {
  // sObject Collections: max 200 records per call
  const CHUNK_SIZE = 200;
  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 operations

```bash
# Input:  Access token, instance URL
# Output: Array of independent results (each succeeds/fails on its own)

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

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

### Data Type Gotchas

- Reference IDs must contain only alphanumeric characters and underscores — no hyphens, dots, or special characters. [src1]
- The `@{refId.field}` notation uses JavaScript-like dot syntax but does NOT support complex expressions or transformations. [src6]
- When using `collateSubrequests=true`, the API may reorder independent subrequests to bulkify DML — do not rely on execution order for subrequests that don't have reference dependencies. [src1]
- sObject Collections `allOrNone=false` returns individual SaveResult per record — you must iterate to find failures. The HTTP response is still 200 even if some records failed. [src4]

## Error Handling & Failure Points

### Common Error Codes

| Code | Meaning | Cause | Resolution |
|---|---|---|---|
| 400 | Bad Request | Malformed JSON, invalid reference ID, unsupported operation | Validate payload structure; check referenceId names match |
| 401 | Unauthorized | Expired or invalid access token | Refresh token or re-authenticate; check session timeout settings |
| PROCESSING_HALTED | Subrequest skipped due to earlier failure | `allOrNone=true` and a prior subrequest failed | Fix the failing subrequest; check field-level security |
| INVALID_FIELD | Field doesn't exist or isn't writable | Wrong API name or insufficient field-level security | Verify field API names via `/sobjects/{Object}/describe` |
| UNABLE_TO_LOCK_ROW | Record locked by concurrent transaction | Two processes updating same record simultaneously | Implement retry with random jitter (100-500ms) |
| LIMIT_EXCEEDED | Governor limit breached | Cumulative SOQL/DML/CPU across subrequests exceeded | Reduce subrequest count or split into separate API calls |
| DUPLICATE_VALUE | Unique constraint violated | Attempting to create duplicate external ID | Use upsert instead of insert; implement idempotency |

[src1, src5, src6]

### Failure Points in Production

- **Governor limit breach from trigger cascading**: Each subrequest can fire triggers, which fire more triggers. A 25-subrequest composite call where each insert fires 5 SOQL queries = 125 SOQL queries, exceeding the 100-query sync limit. Fix: `audit triggers for SOQL/DML counts and use the Limits class to check remaining quota`. [src5, src6]
- **allOrNone=true with validation rules**: If ANY record in the composite fails a validation rule, ALL records are rolled back — including those that passed validation. This is by design but catches teams off-guard. Fix: `pre-validate data client-side before sending composite request`. [src4]
- **Composite Graph cross-graph reference attempt**: Reference IDs are scoped to their graph — `@{ref1.id}` from graph1 is NOT accessible in graph2. This silently fails or returns an error. Fix: `design each graph as a self-contained unit; use separate requests if cross-graph dependencies exist`. [src2, src3]
- **sObject Tree exceeding 5-level depth**: sObject Tree supports max 5 levels (root + 4 children). Attempting deeper nesting returns a cryptic error. Fix: `flatten the hierarchy or use Composite API with reference IDs for deeper nesting`. [src6]
- **collateSubrequests reordering**: When `collateSubrequests=true`, Salesforce may reorder subrequests for efficiency. If your subrequests have implicit dependencies not expressed through reference IDs, this can cause failures. Fix: `always use explicit reference IDs for dependencies; set collateSubrequests=false if order matters`. [src1]
- **Session timeout during large Composite Graph**: A 500-subrequest Composite Graph can take significant time. If the session timeout is set too low, the request may time out mid-execution. Fix: `ensure session timeout is at least 30 minutes; implement retry with idempotent operations`. [src2]

## Anti-Patterns

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

```python
# 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

```python
# 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

```python
# BAD — Composite Batch does NOT support allOrNone or reference IDs
# If subrequest 2 fails, subrequest 1's Account 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

```python
# 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 queries inside Composite when you have more than 5

```python
# BAD — Composite allows max 5 query/collection subrequests out of 25
# This will fail with LIMIT_EXCEEDED if you add 6+ queries
payload = {"compositeRequest": [
    {"method": "GET", "url": "/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 or paginate with queryMore

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

## Common Pitfalls

- **Assuming Composite Graph is one transaction**: The parent Composite Graph request is NOT transactional as a whole — each graph is independently transactional. If graph1 succeeds and graph2 fails, graph1 remains committed. Design accordingly. [src2, src3]
- **Ignoring the 5-query cap in Composite API**: Composite API limits queries and sObject Collections operations to 5 out of 25 subrequests. The remaining 20 must be individual record operations (POST, PATCH, DELETE). [src1]
- **Not handling partial success in Collections**: When `allOrNone=false`, sObject Collections returns HTTP 200 even if individual records failed. You MUST check each SaveResult element's `success` field. [src4]
- **Conflating Composite and Composite Batch**: Composite Batch looks similar but has no reference IDs, no `allOrNone`, and no transactional behavior. It executes each subrequest independently. Choose Composite for dependencies, Batch for independent operations. [src1, src6]
- **Exceeding governor limits with trigger-heavy objects**: If Account has 3 triggers averaging 10 SOQL queries each, creating 4 Accounts via Composite = 120 SOQL queries, exceeding the 100-query sync limit. Pre-calculate trigger overhead. [src5]
- **Using Composite for bulk data loads**: Composite API is for multi-step workflows with cross-references, not bulk data loading. For more than a few hundred records, use Bulk API 2.0. [src7]

## Diagnostic Commands

```bash
# 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 request (dry run via GET query)
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 API version and features available
curl https://{instance_url}/services/data/
```

## Version History & Compatibility

| API Version | Release | Status | Key Composite Features | Notes |
|---|---|---|---|---|
| v66.0 | Spring '26 (Feb 2026) | Current | No new composite features | Legacy hostname redirects removed; Connected App creation disabled by default [src8] |
| v63.0 | Spring '25 (Feb 2025) | Supported | None | — |
| v50.0 | Winter '21 (Oct 2020) | Supported | Composite Graph API introduced | 500 subrequests per graph, per-graph transactions [src3] |
| v43.0 | Summer '18 (Jun 2018) | Supported | sObject Collections introduced | 200-record DML operations [src5] |
| v38.0 | Winter '17 (Oct 2016) | Supported | Composite API introduced | 25 subrequests with reference IDs [src1] |
| v34.0 | Summer '15 (Jun 2015) | Supported | Composite Batch introduced | 25 independent subrequests [src1] |
| v30.0 and below | Pre-2014 | Retired (Jun 2025) | N/A | Minimum supported version is v31.0 [src8] |

[src1, src2, src3, src8]

### Deprecation Policy

Salesforce supports API versions for a minimum of 3 years. Versions are retired in groups — API versions 21.0 through 30.0 were retired in June 2025. Always pin your integration to a specific version and plan to upgrade at least once every 3 years. Monitor the Salesforce Release Notes and API version support matrix before each major release. [src8]

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Creating related records atomically (parent + children in one call) | Bulk data migration (>1,000 records) | Bulk API 2.0 |
| Need to reference output of one operation in a subsequent operation | Operations are completely independent and don't need atomicity | Composite Batch (simpler) |
| Reducing API call consumption (25 operations = 1 API call) | Processing >500 related records needing cross-references | Bulk API 2.0 with external IDs |
| Building multi-step workflows (query, transform, upsert) | Simple single-object CRUD on one record | Standard REST API |
| Creating up to 200 records of the same type efficiently | Need real-time event notification | Platform Events / CDC |
| Need per-group transactional guarantees across large record sets | Need cross-graph transactional guarantees | Split into sequential Composite API calls |

## Important Caveats

- Composite API subrequest limits (25, 500) are hard platform limits and cannot be increased via support tickets or add-on licenses. [src1, src2]
- Governor limits are the real bottleneck — even with 25 subrequests allowed, trigger-heavy orgs may only be able to use 5-10 subrequests before hitting SOQL or DML limits. [src5, src6]
- Sandbox and Developer editions have lower API daily call limits (15K for Developer vs 100K+ for Enterprise) which can mask production-scale issues during testing. [src5]
- The `collateSubrequests` parameter only affects DML ordering optimization — it does not affect the semantics of `allOrNone`. [src1]
- Composite Graph API requires API v50.0 or later. If your integration is pinned to an older version, you cannot use Composite Graph. [src3]
- All composite resources respect field-level security — if the integration user cannot see a field, it will be silently omitted from responses. [src1]
- Rate limits are subject to change with each Salesforce release. Always verify against the current [Salesforce Developer Limits Quick Reference](https://developer.salesforce.com/docs/atlas.en-us.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_api.htm). [src5]

## Related Units

- [Salesforce Bulk API 2.0 Capabilities](/business/erp-integration/salesforce-bulk-api-capabilities/2026) — for high-volume data operations (>2,000 records)
- [Salesforce Governor Limits](/business/erp-integration/salesforce-governor-limits/2026) — deep dive on per-transaction limits
- [Salesforce API Versioning & Deprecation](/business/erp-integration/salesforce-api-versioning-deprecation/2026) — version pinning and retirement schedule
