Integration

Salesforce Composite API vs Composite Batch vs Composite Graph vs sObject Collections

By Rishabh Panwar · 5 min read · Intermediate

An integration log shows HTTP 200 for every Salesforce Composite API call, yet half the records never saved. That bug is common, and it usually comes from treating four different resources as one. Composite, Composite Batch, Composite Graph and sObject Collections all reduce round trips. They behave very differently when it comes to how subrequests relate, what rolls back on failure, and how each call counts against your API allocation.

These resources are one option among several for inbound work; choosing a Salesforce integration pattern shows where they fit. If the REST API itself is new to you, the integration patterns overview has the basics.

The four resources at a glance

ResourceSubrequest limitCan reference earlier results?Failure behaviourCounts against daily API allocation
Composite25 (max 5 queries or sObject Collections)Yes, with @{referenceId}Set allOrNone explicitly: true rolls the whole request back on any failureOnce for the whole request
Composite Batch25NoEach subrequest commits on its own; earlier work staysEach subrequest counts
Composite Graph500 nodes per graphYes, within a graphEach graph saves or rolls back as a unitOnce for the whole request
sObject Collections200 records, one operationNot applicableallOrNone flag decidesOnce per call

Composite: dependent steps in one round trip

Composite runs subrequests in order, and a later subrequest can use the output of an earlier one. The classic example creates an Account and then a Contact that points at it:

{
  "allOrNone": true,
  "compositeRequest": [
    {
      "method": "POST",
      "url": "/services/data/v67.0/sobjects/Account",
      "referenceId": "newAccount",
      "body": { "Name": "Acme Ltd" }
    },
    {
      "method": "POST",
      "url": "/services/data/v67.0/sobjects/Contact",
      "referenceId": "newContact",
      "body": { "LastName": "Rao", "AccountId": "@{newAccount.id}" }
    }
  ]
}

Things to remember:

  • The whole request counts as one API call. That is the main reason to use it.
  • Up to 25 subrequests, and no more than 5 of them can be queries or sObject Collections operations.
  • Set allOrNone on every request. With true the whole Composite request rolls back when any subrequest fails; with false the independent subrequests still save and only the dependants of the failed one are skipped. The docs mark the flag optional without stating a default, so never rely on one. Plain REST calls behave the other way round: each record stands alone unless you ask for all or nothing.
  • Subrequests still share one transaction’s governor limits. Bundling them saves API calls. It does nothing for CPU time, SOQL counts or DML limits.

Composite Batch: unrelated calls bundled together

Composite Batch also takes up to 25 subrequests, but they are independent. You can’t pass data between them, and although they run in the order you list them, each one commits separately. If the third subrequest fails, the first two stay committed, and the batch keeps going unless you set haltOnError. A batch that runs longer than 10 minutes times out and the remaining subrequests are skipped.

It is also a poor bulk tool. Each subrequest counts against rate limits, so bundling saves round trips without saving any allocation.

Use it when you have a handful of unrelated reads or writes and want one network trip instead of several.

Composite Graph: parents and children, saved together

Composite Graph lets you describe a set of related records as a graph. Salesforce works out the order (parents before children) and treats each graph as a single unit: it saves completely or rolls back completely. Several graphs can travel in one payload, and a failure in one graph doesn’t affect the others.

That makes it the natural choice for “create this order with its line items” when you are sending many orders at once.

Composite Graph limits

LimitValue
Graphs per payload75
Maximum depth of a graph15
Nodes per graph500
Nodes per payload, across all graphs500
Distinct node types per payload15
Graph failures before the request stops14

sObject Collections: straightforward bulk CRUD

sObject Collections handles up to 200 records of the same operation in one call. There is no job to create or poll, so it sits neatly between single-record REST and Bulk API 2.0. Above 200 records, move to Bulk API 2.0; the data migration strategy guide covers large loads.

The HTTP 200 trap

A Composite request returns HTTP 200 even when a subrequest failed and the whole request rolled back. The top-level status only tells you the Composite call was processed. The real outcome of each step is in the response body.

{
  "compositeResponse": [
    {
      "referenceId": "newAccount",
      "httpStatusCode": 400,
      "body": [{ "errorCode": "PROCESSING_HALTED", "message": "..." }]
    }
  ]
}

A client that checks only the status code will log success for work that never saved. This shows up in production far more often than it should. Always loop through compositeResponse and check each httpStatusCode.

Picking one

SituationUse
Create an Account, then a Contact that references itComposite
Three unrelated lookups in one network tripComposite Batch
Hundreds of orders, each with line items, each order independentComposite Graph
150 Contact updates with no dependenciessObject Collections
50,000 recordsBulk API 2.0

Comparing them out loud

If you’re asked to compare them, lead with the relationship between subrequests: Composite subrequests can reference each other, Batch subrequests are independent, and Graph organises records into units that save or fail together. Then mention API call counting and the HTTP 200 behaviour. Those two details signal that you have used the API against a real org.

Frequently asked questions

What is the difference between Composite and Composite Batch in Salesforce?

Composite subrequests can reference results from earlier subrequests using @{referenceId} notation and can roll back together. Composite Batch subrequests are independent: they can't share data, and each one commits on its own, so a later failure doesn't undo earlier work.

How many subrequests can a Salesforce Composite request contain?

Up to 25. Of those, no more than 5 can be query operations (Query or QueryAll) or sObject Collections operations.

Does a Composite request count as one API call?

Yes. The entire Composite request counts as a single call against your daily API allocation. Composite Batch is different: each subrequest counts against rate limits.

When should I use Composite Graph?

Use Composite Graph when you need to save related records together, such as a parent and its children, and want each group to succeed or fail as a unit. A single payload can hold up to 75 graphs and 500 nodes in total.

Why does my Composite request return HTTP 200 when a subrequest failed?

The top-level status reflects the Composite call itself. Individual subrequest results, including errors and rollbacks, are inside the response body, so your client must inspect each subrequest's httpStatusCode.