#PowerPlatformTip 47: 'Batch-Method within SharePoint'
TL;DR: Send one
POST _api/$batchwith Send an HTTP request to SharePoint and put many create, update or delete operations into a changeset inside amultipart/mixedbody. One action replaces hundreds of “Create item” calls. The batch call returns 200 even if single operations fail, so check the status line of each part.
💡 Challenge
“Create item” in an Apply to each runs one request per item. For a few hundred rows that takes minutes and uses one action and one SharePoint call per item, which counts against your request limits and can run into throttling.
✅ Solution
The SharePoint REST API supports the OData $batch option. A batch request is one HTTP call whose body contains several operations. Read operations sit directly in the batch, write operations (create, update, delete) are wrapped in a changeset. SharePoint runs every operation and returns one multipart response.
🔧 How It’s Done
1. Know the body format.
🔸 This body creates two items in the list “Tasks”. Every URL inside the body must be absolute and point to the same site as the request:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
--batch_abc
Content-Type: multipart/mixed; boundary="changeset_xyz"
--changeset_xyz
Content-Type: application/http
Content-Transfer-Encoding: binary
POST https://contoso.sharepoint.com/sites/Team/_api/web/lists/getbytitle('Tasks')/items HTTP/1.1
Content-Type: application/json;odata=nometadata
{"Title":"Item 1"}
--changeset_xyz
Content-Type: application/http
Content-Transfer-Encoding: binary
POST https://contoso.sharepoint.com/sites/Team/_api/web/lists/getbytitle('Tasks')/items HTTP/1.1
Content-Type: application/json;odata=nometadata
{"Title":"Item 2"}
--changeset_xyz--
--batch_abc--
🔸 Update an item: PATCH .../items(5) HTTP/1.1 with the headers Content-Type: application/json;odata=nometadata and IF-MATCH: *, followed by the changed fields.
🔸 Delete an item: DELETE .../items(5) HTTP/1.1 with the header IF-MATCH: * and no body.
2. Build the parts with a Select.
🔸 Compose NL: decodeUriComponent('%0D%0A') (a line break, CRLF).
🔸 Select over your source rows, switch the map to text mode and build one part per row:
1
2
3
4
5
6
concat('--changeset_xyz', outputs('NL'),
'Content-Type: application/http', outputs('NL'),
'Content-Transfer-Encoding: binary', outputs('NL'), outputs('NL'),
'POST https://contoso.sharepoint.com/sites/Team/_api/web/lists/getbytitle(''Tasks'')/items HTTP/1.1', outputs('NL'),
'Content-Type: application/json;odata=nometadata', outputs('NL'), outputs('NL'),
string(item()), outputs('NL'))
🔸 item() must be an object with internal column names, for example {"Title":"Item 1","Status":"Open"}.
3. Wrap the parts into the batch body.
🔸 Compose BatchBody:
1
2
3
4
5
concat('--batch_abc', outputs('NL'),
'Content-Type: multipart/mixed; boundary="changeset_xyz"', outputs('NL'), outputs('NL'),
join(body('Select'), ''),
'--changeset_xyz--', outputs('NL'),
'--batch_abc--')
4. Send the batch.
🔸 Send an HTTP request to SharePoint: Site Address = your site, Method POST, Uri _api/$batch.
🔸 Headers: Content-Type = multipart/mixed; boundary=batch_abc, Accept = application/json;odata=nometadata.
🔸 Body: outputs('BatchBody').
5. Check the response.
🔸 The response is multipart text, not JSON, so Parse JSON cannot read it. Each operation has its own status line: HTTP/1.1 201 Created for a create, HTTP/1.1 204 No Content for an update or delete, 4xx or 5xx for a failure. Search the response for status lines other than 201 or 204 and log or retry those rows.
🎉 Result
One HTTP action writes a whole set of items. Instead of hundreds of “Create item” runs inside a loop, the flow sends one request per chunk and finishes in a fraction of the time.
🌟 Key Advantages
🔸 Fewer actions: one HTTP action per batch instead of one action per item.
🔸 Faster runs: no per-item round trips inside an Apply to each.
🔸 Mixed operations: create, update and delete in the same changeset.
🔸 Standard connector: “Send an HTTP request to SharePoint” is part of the standard SharePoint connector.
⚠️ Good to Know
🔸 Not transactional: if one operation in a changeset fails, the others still complete and are not rolled back.
🔸 Chunk large sets: split big arrays with chunk() (for example 500 rows) and send one batch per chunk, so each request stays small and a failure only affects one chunk.
🔸 No file uploads: uploading several files in one batch is not supported.
🔸 Same site only: all URLs in the body must target the site in the request URL, and URLs are case-sensitive.
🛠️ FAQ
1. Why does the whole batch return 200 although some items failed?
The 200 only says that SharePoint processed the batch. The result of each operation is in its own part of the response, so always check the status line of every part.
2. Can I mix create, update and delete in one batch?
Yes. Put all write operations into the same changeset, each as its own part with its own method, URL and headers.
3. Do I need __metadata with the list item type?
No, not with Content-Type: application/json;odata=nometadata. Then the body only contains the fields with their internal names.
🔗 Related Tips
🔸 #PowerPlatformTip 97: ‘Enhanced Pagination’ - read large lists in bulk before you write them back in batches.
🔸 #PowerPlatformTip 95: ‘Optimized SharePoint Queries’ - reduce the data you have to process with better OData filters.
This tip is by Marcel Lehmann, Microsoft MVP (Business Applications) and founder of the Power Platform Academy – live Power Platform workshops, free or CHF 49. Join the workshop waitlist to get notified about new dates. For companies that want this implemented for them, see KMUpower – Power Platform consulting & automation for SMEs (DACH).