In one sentence: the Composite API lets you send several REST requests in a single HTTP round trip, optionally as one all-or-nothing transaction, with later subrequests able to reference IDs and fields returned by earlier ones. It exists to stop you making twelve calls to create an account, its contacts and its opportunity, and the subrequest ceiling is low enough that it is a batching tool, not a bulk loading tool.
The four resources, and which one you want
/compositeruns up to 25 subrequests in order, in one transaction if you ask for it, and lets each subrequest use the output of an earlier one. This is the one people mean./composite/batchruns up to 25 independent subrequests. No referencing, no shared transaction. Useful for reading several unrelated things at once./composite/treecreates a parent and its children in one go from a nested structure, up to a fixed total record count and a small nesting depth. Good for a known hierarchy, inflexible for anything else./composite/sobjects, the sObject Collections resource, creates, updates or deletes up to 200 records of the same operation in a single call. When you have 200 similar records, this beats composite.
Within /composite, only a small number of the 25 subrequests may themselves be query or sObject Collections calls, so you cannot dodge the limits by nesting one inside the other.
Referencing an earlier result
Every subrequest carries a referenceId. Later subrequests use @{referenceId.field} to pull a value out of that response, most often the new record’s ID:
{
"allOrNone": true,
"compositeRequest": [
{
"method": "POST",
"url": "/services/data/v63.0/sobjects/Account",
"referenceId": "newAccount",
"body": { "Name": "Northwind Ltd" }
},
{
"method": "POST",
"url": "/services/data/v63.0/sobjects/Contact",
"referenceId": "newContact",
"body": {
"LastName": "Okafor",
"AccountId": "@{newAccount.id}"
}
},
{
"method": "GET",
"url": "/services/data/v63.0/sobjects/Contact/@{newContact.id}?fields=Id,Name,AccountId"
}
]
}
Two details that catch people out. The reference key is the field name as it appears in the response body, and for a create that is lowercase id, not Id. And references work in the URL as well as the body, which is how the third subrequest above reads back the record it just made.
allOrNone is the real reason to use it
With allOrNone set to true, a failure anywhere rolls the whole thing back. That gives you something the plain REST API cannot: a client-driven transaction spanning several objects. Without it, subrequests that already succeeded stay committed and you are left reconciling a half-created hierarchy from the client.
If your integration creates related records that are meaningless apart, set it to true and handle the single failure response. If you are pushing independent records and want maximum throughput, set it to false and process the per-subrequest results.
The response is an array in the same order as your request, each entry carrying its own HTTP status code and body. Check every one. A 200 on the outer call says the composite request was processed, not that your records saved.
What it does not fix
Governor limits still apply as if the subrequests ran normally, and a composite call counts as a single API request against your daily allowance, which is the other reason integrations reach for it. Triggers, flows and validation rules run for each subrequest. A composite request that fires 25 creates on an object with a heavy trigger is 25 trigger executions inside one transaction, and the shared limits are what usually bites.
It is also not a loading mechanism. Twenty-five subrequests, or 200 records through sObject Collections, is nothing against a migration. For volume, use Bulk API 2.0 and accept the asynchrony.
When to reach for it
Mobile and web clients on poor connections, where round trips dominate. Integrations creating small object graphs, where the transaction matters more than the throughput. Anything currently making a call to get an ID and then a second call to use it. If your middleware is doing a chatty create-then-link dance, this is the fix.
Frequently asked
Can I mix methods in one composite request? Yes. GET, POST, PATCH, DELETE and query subrequests can appear together, and they run in the order you list them.
Does a failed subrequest stop the rest? With allOrNone true, yes, and everything rolls back. With it false, later subrequests still run, which can mean a reference resolves to nothing and produces a second, confusing error.
Is there an Apex equivalent? Not for calling in. Inside Apex you are already in one transaction. Going out, you would use chained callouts, which is a different problem.
Does it help with API limit consumption? Yes, substantially. One composite call is one request against the daily limit regardless of how many subrequests it carries.
