Back in 2021, I wrote a popular article showing how to batch create items in SharePoint using Power Automate. Since then, the chunk() expression has been introduced which makes batching far simpler.
In this post, I will show you how it works on a CSV file of 15,000 rows.
This method is:
- Faster
- Simpler – no variables or complex expressions.
- More efficient – Has fewer steps and less API actions.
Example Data
The CSV file used in the example, which you can download here, looks like this:
Customer,Invoice Number,Invoice Amount Skibox,1,3051.83 Jabberstorm,2,4121.73 Yoveo,3,355.97 BlogXS,4,1606.71 Fanoodle,5,1680.15
New Method
The new method is similar to the one in the previous blog post, but makes use of the new chunk() expression. This function enabled the removal of the variable controlled loop and complex expressions.
Here is a visual overview of the flow, as you can see, it is quite simple:
SharePoint Setup
If you want to follow along with the sample data, setup a SharePoint List with the following fields:
- Customer – Single Line of Text.
- InvNum – Number – No Decimal Places.
- InvoiceAmount – Number – Two Decimal Places.
How the flow works
Settings (Parse JSON)
A single place to configure the settings for the flow:
- Tenant URL, Site Path and List Name
- Batch Size
- oData Level
You will need to edit the contents of this action to configure it for your environment.
Get File Content and GenerateSPData
Here the content from the CSV file is loaded and then used as the input to the GenerateSPData, which is a select action. I used an expression to trim the contents of the CSV and then split it on a new line character.
The select then gets each field from the CSV and generates a clean array of objects which exactly match SharePoint internal column names.
This is the step where you will reshape your own data to fit with your columns.
Batch Request Template (Compose)
Prepares a single changeset POST template with a |RowData|
placeholder:
/POST /{sitePath}/_api/web/lists/getByTitle('{listName}')/items Content-Type: application/json;odata={oDataLevel} Prefer: return=minimal |RowData|
This keeps the HTTP boilerplate out of the loop logic.
Chunking & per-batch preparation (For each batch)
The chunk()
expression splits the transformed items into arrays of size batchSize.
PrepBatch
(another Select) replaces |RowData|
with the stringified item for each row, yielding a list of POST parts ready for the changeset.
Send to SharePoint ($batch)
Posts a single multipart/mixed request to /_api/$batch
containing one changeset with all the item creates for that chunk. This minimizes API calls and is much faster than per-item requests.
Why this method is superior to the previous version
- Greatly Simplified – The loop control has been removed completely which allowed the removal of several complex expressions.
- No Variables – Everything is expressed as direct transformations in
Select
, which makes the flow easier to maintain. - Smaller Payload – The payload size has been slightly reduced which should result in slightly improved performance.
Note on CSV parsing
This example uses basic string splitting. If your CSV includes quoted fields, embedded commas, or newlines, swapping this step for My CSV parser keeps the exact same downstream mapping pattern, but with resilient parsing for more complex CSV files.
Choosing the Right Batch Size
It can be tempting to set the batch size to 1,000 items in order to minimise the number of API calls. In practice, this usually causes more problems than it solves:
- Payload size limits – Large batches can quickly exceed SharePoint’s maximum payload size for the
$batch
endpoint, especially if your list items contain multiple columns or large text fields. - Debugging difficulty – When something goes wrong, isolating the error in a 1,000-item batch is far harder than in a smaller, more manageable batch.
From experience, I’ve found that 250 items per batch strikes the right balance:
- It delivers nearly all of the performance benefit compared to much larger batches.
- It keeps requests lightweight, reliable, and easier to troubleshoot.
If your payload is very simple (e.g., only a few fields of plain text or numbers), you might safely increase the batch size to around 500. But going beyond that provides diminishing returns while increasing the risk of hitting limits.
An Even Easier Way with PowerTools
Everything shown above can be built natively in Power Automate, but if you want to make it even simpler, Power Tools for Power Automate lets you do the same job in a single action.
Instead of creating a scope with multiple steps, you can:
- Drop in the PowerTools Batch Create SharePoint Items action.
- Point it at your target site and list.
- Supply your array of data (for example, rows parsed from a CSV file).
That’s it — PowerTools automatically handles the batching, chunking, and API calls behind the scenes.
This makes your flows cleaner, faster to build, and easier to maintain, while still giving you the same performance and reliability.
The entire flow above, including parsing the CSV becomes as simple as this:
In addition to the batching actions, Power Tools offers many actions that make building flows faster and easier. Check out the full list of capabilities here.
Flow Code
Because there are no variable in this flow, you can copy the code below and paste it into your own flow. You’ll then have a completely functional flow which only needs the settings and connections updated.
Important Note: You will not be able to paste this scope into the new designer. You will need to go into the classic designer:
- Copy the code below into your clipboard.
- Add a new action in the classic desginer.
- Click on My Clipboard
- Press CTRL-V
- Click on the Scope named Create SharePoint Items
- Modify the settings as per your own environment.
{ "id": "29ab039d-eccc-4442-a7bd-b8853228a457", "brandColor": "#8C3900", "connectionReferences": { "shared_powertools-5fc8929d8f1923503b-5f2f2ee6f39ab8efad": { "connection": { "id": "/providers/Microsoft.PowerApps/apis/shared_powertools-5fc8929d8f1923503b-5f2f2ee6f39ab8efad/connections/shared-powertools-5f-c52fe3e7-1ac2-4f14-be7e-87b210125878" } }, "shared_onedriveforbusiness": { "connection": { "id": "/providers/Microsoft.PowerApps/apis/shared_onedriveforbusiness/connections/b728329fd30647b4ab6ade7e3a033f2e" } }, "shared_sharepointonline": { "connection": { "id": "/providers/Microsoft.PowerApps/apis/shared_sharepointonline/connections/dd614c719a574e8788a7d3de1dc00b7b" } } }, "connectorDisplayName": "Control", "icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDMyIDMyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPg0KIDxwYXRoIGQ9Im0wIDBoMzJ2MzJoLTMyeiIgZmlsbD0iIzhDMzkwMCIvPg0KIDxwYXRoIGQ9Im04IDEwaDE2djEyaC0xNnptMTUgMTF2LTEwaC0xNHYxMHptLTItOHY2aC0xMHYtNnptLTEgNXYtNGgtOHY0eiIgZmlsbD0iI2ZmZiIvPg0KPC9zdmc+DQo=", "isTrigger": false, "operationName": "Create_SharePoint_Items", "operationDefinition": { "type": "Scope", "actions": { "batchTemplate": { "type": "Compose", "inputs": "--changeset_c1\nContent-Type: application/http\nContent-Transfer-Encoding: binary\n\nPOST /@{body('Settings')?['sitePath']}/_api/web/lists/getByTitle('@{body('Settings')?['listName']}')/items HTTP/1.1\nContent-Type: application/json;odata=@{body('Settings')?['oDataLevel']}\nPrefer: return=minimal\n\n|RowData|\n", "runAfter": { "GenerateSPData": [ "Succeeded" ] }, "metadata": { "operationMetadataId": "7510be95-4ad3-4c3b-8d0f-8ce2ea5fe270" } }, "Settings": { "type": "ParseJson", "inputs": { "content": { "tenantBaseUrl": "accendo1.sharepoint.com", "sitePath": "sites/PowerAutomateText", "listName": "Invoices Development Test", "batchSize": 350, "oDataLevel": "nometadata" }, "schema": { "type": "object", "properties": { "tenantBaseUrl": { "type": "string", "title": "Base URL of SharePoint Site", "description": "Base URL of SharePoint Site" }, "sitePath": { "type": "string", "title": "Relative path of the site" }, "listName": { "type": "string", "title": "Sharepoint List", "description": "List where items are to be batch created" }, "batchSize": { "type": "integer", "title": "Batch Size", "description": "Number of items to send in a single batch" }, "oDataLevel": { "type": "string", "title": "oData Level", "description": "verbose, minimalmetadata or nometadata" } } } }, "runAfter": {}, "trackedProperties": { "meta": { "type": "SP.Data.@{outputs('settings')?['listName']}ListItem" }, "batchGUID": "@{guid()}", "changeSetGUID": "@{guid()}" }, "metadata": { "operationMetadataId": "e2c45d0b-2dd7-458c-af66-4aaba453a85c" } }, "Each_Batch": { "type": "Foreach", "foreach": "@chunk(body('GenerateSPData'), body('Settings')?['batchSize'])", "actions": { "PrepBatch": { "type": "Select", "inputs": { "from": "@items('Each_Batch')", "select": "@replace(outputs('batchTemplate'), '|RowData|', string(item()))" }, "runAfter": {}, "metadata": { "operationMetadataId": "0f90539a-b12a-4080-af7b-f54a5562333a" } }, "sendBatch": { "type": "ApiConnection", "inputs": { "host": { "connection": { "name": "@json(decodeBase64(triggerOutputs().headers['X-MS-APIM-Tokens']))['$connections']['shared_sharepointonline']['connectionId']" } }, "method": "post", "body": { "method": "POST", "uri": "/_api/$batch", "headers": { "X-RequestDigest": "digest", "Content-Type": "multipart/mixed;boundary=batch_b1" }, "body": "--batch_b1\nContent-Type: multipart/mixed; boundary=\"changeset_c1\"\nContent-Transfer-Encoding: binary\n\n@{join(body('PrepBatch'), decodeUriComponent('%0A'))}\n--changeset_c1--\n\n--batch_b1--" }, "path": "/datasets/@{encodeURIComponent(encodeURIComponent('https://',body('Settings')?['tenantBaseUrl'],'/',body('Settings')?['sitePath'],'/'))}/httprequest", "authentication": { "type": "Raw", "value": "@json(decodeBase64(triggerOutputs().headers['X-MS-APIM-Tokens']))['$ConnectionKey']" } }, "runAfter": { "PrepBatch": [ "Succeeded" ] }, "metadata": { "operationMetadataId": "cd8e1c16-ce3f-4626-96f6-74175878a718", "flowSystemMetadata": { "swaggerOperationId": "HttpRequest" } } }, "Result": { "type": "Compose", "inputs": "@base64ToString(body('sendBatch')['$content'])", "runAfter": { "sendBatch": [ "Succeeded" ] }, "metadata": { "operationMetadataId": "d3eab115-bee1-4d3f-ae84-5573cde48b04" } } }, "runAfter": { "batchTemplate": [ "Succeeded" ] }, "metadata": { "operationMetadataId": "4e0e72da-48b8-40b9-a824-d948006e98a8" } }, "GenerateSPData": { "type": "Select", "inputs": { "from": "@take(skip(split(trim(body('Get_file_content')), decodeUriComponent(decodeUriComponent('%0A'))),1),100)", "select": { "Customer": "@split(item(), ',')[0]", "InvNum": "@int(split(item(), ',')[1])", "InvoiceAmount": "@split(item(), ',')[2]" } }, "runAfter": { "Get_file_content": [ "Succeeded" ] }, "metadata": { "operationMetadataId": "7f2df4b2-3704-4b7e-9be4-25baf2e005ac" } }, "Get_file_content": { "type": "ApiConnection", "inputs": { "host": { "connection": { "name": "@json(decodeBase64(triggerOutputs().headers['X-MS-APIM-Tokens']))['$connections']['shared_onedriveforbusiness']['connectionId']" } }, "method": "get", "path": "/datasets/default/files/@{encodeURIComponent(encodeURIComponent('b!Xm9bgFp6o0yRZXE2CnS2bsRojWZetglOshpzrfOcZOZ3RtFM3COMT4fTC4d_A2tk.01CM5RGF6C5YHGIXWZERCLCOADO5MDURKV'))}/content", "queries": { "inferContentType": true }, "authentication": { "type": "Raw", "value": "@json(decodeBase64(triggerOutputs().headers['X-MS-APIM-Tokens']))['$ConnectionKey']" } }, "runAfter": { "Settings": [ "Succeeded" ] }, "metadata": { "b!Xm9bgFp6o0yRZXE2CnS2bsRojWZetglOshpzrfOcZOZ3RtFM3COMT4fTC4d_A2tk.01CM5RGF6C5YHGIXWZERCLCOADO5MDURKV": "/ExcelMockData.txt", "operationMetadataId": "ea8fd25e-7432-407a-98af-90cde9f799fc", "flowSystemMetadata": { "swaggerOperationId": "GetFileContent" } } } }, "runAfter": {}, "metadata": { "operationMetadataId": "301650a5-14dd-4a16-9a19-514c36f2b009" } } }
Conclusion
This new approach makes batch-creating SharePoint list items in Power Automate much faster, cleaner, and easier to maintain. By using the chunk()
function and the Select action to shape your data directly, you avoid variables, reduce complexity, and cut down on unnecessary API calls.
Even with large files, the flow stays reliable and efficient. With the right batch size (I recommend around 250), you get nearly all the performance benefits while avoiding the risks of oversized requests.
If you built flows using my original 2021 method, I encourage you to give this new approach a try, you’ll find it dramatically simpler.
If you want the very easiest, maker friendly method of SharePoint Batching, Power Tools for Power Automate is the way to go.