In Part 1 of this series (Batch Process Records SpreadsheetWeb API Power Query), we demonstrated how to connect Excel to the SpreadsheetWeb API using Power Query to send a single request and retrieve calculated results from an Excel-based web application.
But what happens when you need to process multiple records at once?
In real-world scenarios—especially in insurance quoting and underwriting workflows—you often have to run the same pricing model for dozens, even hundreds, of customer records. Manually triggering single API calls for each input row quickly becomes inefficient and time-consuming.
That’s where batch processing and the calculatemultiple API method come in.
Why Multi-Record Processing Matters
When working with Excel-based rating models via SpreadsheetWeb, it’s common to feed in customer data from external sources—think of:
- A list of customer profiles for bulk quote generation
- Policy renewals needing re-rating
- Book-of-business repricing efforts
- Testing multiple scenarios for underwriting analysis
Rather than send one API call per row—which is possible but adds complexity and slows performance—SpreadsheetWeb offers a dedicated method to handle batch processing more efficiently.
Two Ways to Process Multiple Records via API
Looping Through Records in Power Query
In this approach, you loop through each row of your Excel table, calling the API once per record using Power Query’s List.Transform or Table.AddColumn functions.
While this method works, it comes with drawbacks:
- Performance impact: Each API call introduces latency and overhead.
- Complexity: You must handle individual error responses, retries, and rate limiting per row.
- Code maintenance: Managing a loop of web requests in Power Query can get messy.
Using calculatemultiple for Batch Requests
To simplify things, SpreadsheetWeb provides the calculatemultiple endpoint, which allows you to send multiple input records in a single API request.
This method has several advantages:
- Better performance – Fewer requests = faster results
- Cleaner implementation – One request, one response
- Easier error handling – Responses are grouped and indexed
How calculatemultiple Works
The calculatemultiple API method expects a list of input objects, each representing a set of input fields. Here's the basic structure:
{
"request": {
"requestId": "UNIQUE-ID-HERE", // Unique identifier for the request
"workspaceId": "your-workspace-id-here", // Replace with your actual Workspace ID
"applicationId": "your-application-id-here", // Replace with your actual Application ID
"localization": {
"decimalSeparator": ".", // Defines decimal separator (e.g., "." or ",")
"thousandsSeparator": ",", // Defines thousands separator (e.g., "," or ".")
"dateSeparator": "/", // Defines date separator (e.g., "/" or "-")
"currencySign": "$" // Defines currency sign (e.g., "$", "€", "₺")
},
"inputs": {
"Scenario1": {
"calculation": {
"inputs": [
{
"reference": "Input1", // The named range in your spreadsheet to populate
"value": [
[
{
"type": 0,
"formatType": 0,
"format": "General",
"text": "5000", // Displayed as text (optional)
"value": "5000", // Actual value used in calculation
"overwrite": 1 // 1 means it will overwrite existing data
}
]
],
"overwrite": 1
}
],
"outputs": [
"Output1" // Named range to retrieve the result from
],
"dynamicOutputs": [],
"validations": [
{
"reference": "Input1",
"retrieveValues": true,
"validate": true
}
],
"overwrite": 1
},
"goalSeek": {
"enabled": true,
"targetRef": "Output1", // The desired output cell reference
"changingRef": "Input1", // The input cell to be modified
"targetValue": 10000, // Desired output value
"maxIterations": 100, // Maximum number of iterations
"maxChange": 0.001, // Maximum change per iteration
"algorithm": 1 // Goal Seek algorithm (1 = basic, 2 = advanced)
},
"solver": {
"enabled": true,
"objective": "Output1", // Cell to maximize or minimize
"optimizationType": 1, // 1 = Maximize, 2 = Minimize
"valueOfGoal": 10000, // Target value for objective (optional)
"nonNegative": true, // True = result must be >= 0
"variables": [
"Input1"
],
"constraints": [
{
"operator": 1 // 1 = <=, 2 = >=, 3 = =
// Additional constraint parameters can be added
}
]
}
}
},
"recordInfos": {
"Scenario1": {
"id": 0,
"editId": 0,
"entryDate": "2025-04-11T11:13:35.297Z",
"lastUpdate": "2025-04-11T11:13:35.297Z",
"saveAsNewRecord": true,
"tags": {},
"loadExistingRecord": false,
"tagSavingBehavior": {
"assignTagsForSavingUser": false,
"assignTagsForAssociatedUserTags": false,
"assignSpecificTags": "",
"mergeTagsWhenUpdatingRecords": false,
"assignTagsByNamedRange": "",
"assignTagsByNamedRangeSequenceId": 0,
"retainExistingTags": false,
"tagsToAssign": []
}
}
},
"connectionString": "", // Optional external DB connection string
"friendlyTableTranslations": {},
"events": {
"print": [],
"excelExport": [],
"email": [],
"goalSeek": 0,
"solver": 0,
"queries": []
},
"transactionSequenceId": 0,
"transactionDate": "2025-04-11T11:13:35.297Z",
"executeEvents": true
}
}
The API returns a list of corresponding output objects, preserving the order of input records.
Implementing Batch API Calls in Power Query
Let’s walk through the basic steps to implement this in Power Query.
Prepare Your Data Table
Ensure your Excel table (e.g., InputData) contains all required fields as columns, with each row representing a distinct input scenario.
Get Bearer Token for /calculatemultiple API Call
To make an API call, a bearer token is needed. In this case, at the very beginning of code, a bearer token must be gotten.
TokenUrl = "https://identity.spreadsheetweb.com/connect/token", TokenBody = "grant_type=client_credentials&client_id=YOUR-CLIENTID&client_secret=YOUR-CLIENTSECRET", TokenResponse = Web.Contents(TokenUrl, [ Headers = [#"Content-Type" = "application/x-www-form-urlencoded"], Content = Text.ToBinary(TokenBody) ]), TokenJson = Json.Document(TokenResponse), AccessToken = TokenJson[access_token],
Transform Input Rows into a JSON Payload
Use Power Query to format your input rows into the data.inputs format expected by the API:
InputTable = Excel.CurrentWorkbook(){[Name="input_grid"]}[Content],
// clean necessary columns
CleanTable = Table.TransformColumns(InputTable, {
{"Base Policy Face Amount", each Text.Replace(Text.Replace(Text.From(_), "$", ""), ",", ""), type text},
{"Children's Insurance Rider Amount", each Text.Replace(Text.Replace(Text.From(_), "$", ""), ",", ""), type text},
{"Age", each Text.From(_), type text},
{"Premium Period", each Text.From(_), type text}
}),
// reference matches
ReferenceList = {
{"State", "State"},
{"PlanType", "Plan Type"},
{"PremiumPeriod", "Premium Period"},
{"IssueAge", "Age"},
{"Gender", "Gender"},
{"Class", "Tobacco Usage"},
{"PremiumMode", "Premium Mode"},
{"FaceAmount", "Base Policy Face Amount"},
{"ChildrenRiderAmount", "Children's Insurance Rider Amount"},
{"AccidentalDeath", "Accidental Death Benefit Rider"},
{"WaiverPremium", "Waiver of Premium"}
},
Make the API Request
Pass the JSON payload to the calculatemultiple endpoint using Web.Contents:
// API Request Body
RequestBody = Json.FromValue([
request = [
workspaceId = "YOUR-WORKSPACEID",
applicationId = "YOUR-APPLICATIONID",
transactionSequenceId = 2,
inputs = IndexedInputs
]
]),
RequestBodyText = Text.FromBinary(RequestBody),
// API call
ApiResponse = Web.Contents("https://api.spreadsheetweb.com/calculations/calculatemultiple", [
Headers = [
#"Content-Type" = "application/json",
Authorization = "Bearer " & AccessToken
],
Content = RequestBody
]),
ApiResponseText = Text.FromBinary(ApiResponse),
JsonResponse = Json.Document(ApiResponse),
Outputs = if Record.HasFields(JsonResponse, "response") and Record.HasFields(JsonResponse[response], "outputs")
then JsonResponse[response][outputs]
else error " There is no 'outputs' field in the API response. The request format may be incorrect.",
Parse and Expand the Results
The API response will be a list of output records. Use Power Query’s Record and List functions to convert this into a table you can work with in Excel:
PremiumList = List.Transform(
Record.ToList(Outputs),
each try
let
OutList = _[calculation][outputs],
PremiumObj = List.First(List.Select(OutList, each _[reference] = "TotalPremium")),
Val = PremiumObj[value]{0}{0}[value]
in
Val
otherwise null
),
// output table
ResultTable = Table.FromColumns({PremiumList}, {"TotalPremium"})
in
ResultTable
You can then expand the columns to expose the actual result fields.
Full Code for Batch Process Records SpreadsheetWeb API Power Query:
let
// 1) Get the access token
TokenUrl = "https://identity.spreadsheetweb.com/connect/token",
TokenBody = "grant_type=client_credentials&client_id=YOUR-CLIENT-ID&client_secret=YOUR-CLIENT-SECRET",
TokenResponse = Web.Contents(TokenUrl, [
Headers = [#"Content-Type" = "application/x-www-form-urlencoded"],
Content = Text.ToBinary(TokenBody)
]),
TokenJson = Json.Document(TokenResponse),
AccessToken = TokenJson[access_token],
// 2) Read the input table from Excel
InputTable = Excel.CurrentWorkbook(){[Name="input_grid"]}[Content],
// 3) Clean and format necessary columns
CleanTable = Table.TransformColumns(InputTable, {
{"Base Policy Face Amount", each Text.Replace(Text.Replace(Text.From(_), "$", ""), ",", ""), type text},
{"Children's Insurance Rider Amount", each Text.Replace(Text.Replace(Text.From(_), "$", ""), ",", ""), type text},
{"Age", each Text.From(_), type text},
{"Premium Period", each Text.From(_), type text}
}),
// 4) Define mapping between API references and Excel column names
ReferenceList = {
{"State", "State"},
{"PlanType", "Plan Type"},
{"PremiumPeriod", "Premium Period"},
{"IssueAge", "Age"},
{"Gender", "Gender"},
{"Class", "Tobacco Usage"},
{"PremiumMode", "Premium Mode"},
{"FaceAmount", "Base Policy Face Amount"},
{"ChildrenRiderAmount", "Children's Insurance Rider Amount"},
{"AccidentalDeath", "Accidental Death Benefit Rider"},
{"WaiverPremium", "Waiver of Premium"}
},
// 5) Build input objects in dictionary format
IndexedInputs =
let
InputRows = Table.ToRows(CleanTable),
InputObjects = List.Transform(InputRows, each
let
RowRecord = try Record.FromList(
List.Transform(_, each if _ = null then "N/A" else _),
List.Transform(ReferenceList, each _{1})
) otherwise null,
InputsArray = if RowRecord <> null then
List.Transform(ReferenceList, (pair) =>
[
reference = pair{0},
value = {
{
[
type = 0,
formatType = 0,
format = "General",
text = try Text.From(Record.Field(RowRecord, pair{1})) otherwise "N/A",
value = try Text.From(Record.Field(RowRecord, pair{1})) otherwise "N/A"
]
}
}
]
)
else {},
CalcObj = if RowRecord <> null then
[
calculation = [
inputs = InputsArray,
outputs = {"TotalPremium"}
]
]
else null
in
CalcObj
),
CleanInputObjects = List.RemoveNulls(InputObjects),
InputKeys = List.Transform({0..List.Count(CleanInputObjects)-1}, each Text.From(_)),
FinalInputs = Record.FromList(CleanInputObjects, InputKeys)
in
FinalInputs,
// 6) Construct the API request body
RequestBody = Json.FromValue([
request = [
workspaceId = "YOUR-WORKSPACE-ID",
applicationId = "YOUR-APPLICATION-ID",
transactionSequenceId = 1,
inputs = IndexedInputs
]
]),
RequestBodyText = Text.FromBinary(RequestBody),
// 7) Make the API request
ApiResponse = Web.Contents("https://api.spreadsheetweb.com/calculations/calculatemultiple", [
Headers = [
#"Content-Type" = "application/json",
Authorization = "Bearer " & AccessToken
],
Content = RequestBody
]),
ApiResponseText = Text.FromBinary(ApiResponse),
JsonResponse = Json.Document(ApiResponse),
// 8) Extract outputs
Outputs = if Record.HasFields(JsonResponse, "response") and Record.HasFields(JsonResponse[response], "outputs")
then JsonResponse[response][outputs]
else error "The 'outputs' field is missing in the API response. Please verify the request format.",
// 9) Extract TotalPremium values from the response
PremiumList = List.Transform(
Record.ToList(Outputs),
each try
let
OutList = _[calculation][outputs],
PremiumObj = List.First(List.Select(OutList, each _[reference] = "TotalPremium")),
Val = PremiumObj[value]{0}{0}[value]
in
Val
otherwise null
),
// 10) Return as a table
ResultTable = Table.FromColumns({PremiumList}, {"TotalPremium"})
in
ResultTable
Conclusion
By using calculatemultiple and Power Query, you can scale your Excel-based insurance quoting process to handle hundreds of requests at once—all from within Excel.
This approach is faster, more maintainable, and ideal for:
- Batch quoting
- Renewal repricing
- Book-of-business analytics
- Underwriting automation
What’s Next?
If you're interested in seeing a working example or receiving a sample file, contact us and we’ll be happy to help.
Want to try it yourself? Head over to your SpreadsheetWeb workspace, generate your API key, and begin integrating your models in batch mode today!