Azure DevOps REST API - Why does it take so long to retrieve parent/children work items?

Regular Visitor

I am creating a core MVC application to retrieve all Features and child PBIs from an Area Path and Iteration path. I am using the Get Work Item and Get Workitems Batch REST calls. When retrieving the data my solution is as follows:

  1. Get parent Feature data -- and a separate int[] list of the Parent Ids
  2. foreach id in the int[] list, call WorkitemBatch {ids, fields}
  3. iterate the child valies nd put in a child list

Running the code is successful, but retrieving 11 parent records with approximately 60-80 child records takes approximately 12 seconds to return. Is there a more performant process?



public async Task<List<ComplexWorkItem>> GetParentAndChildrenFields(RequestWithToken request, int[] wkItmIds)
string idList = string.Join(",", wkItmIds.Select(p => p.ToString()));

//Get original URI
string baseURI = request.RequestURI;

//expand the list of retrieved values --get everything you can
request.RequestURI = baseURI + $@"/_apis/wit/workitems?ids={idList}&$expand=all&api-version=6.0";
request.RequestBody = String.Empty;

string jsonString = await GetHttpResponseString(request);
InternalComplexWorkItem expandedWorkItem = JsonSerializer.Deserialize<InternalComplexWorkItem>(jsonString);
List<ComplexWorkItem> parentChildWorkItems = new List<ComplexWorkItem>();

int relatedIdsCount = 0;

//Iterate through the workitems
foreach (ExpandedWorkItemValue workItemParent in expandedWorkItem.value)
ComplexWorkItem complexWorkItem = new ComplexWorkItem();
WorkItemPart parentWorkItemParticle = new WorkItemPart();
//Add the parent to an itn array for use
//in the GetExpandedWorkItemIDs method
int[] parentId = new int[1];

//put back to baseURI
request.RequestURI = baseURI;

parentId[0] =;
//Get the parent fields
InternalComplexWorkItem parentValue = await GetExpandedWorkItemIDs(request, parentId);
//Add to the parent segment of the ComplexWorkItem
#region Parent Segment
parentWorkItemParticle.WorkItemId = parentValue.value[0].id.ToString();
parentWorkItemParticle.WorkItemType = parentValue.value[0].fields.WorkItemType;
parentWorkItemParticle.WorkItemState = parentValue.value[0].fields.State;
parentWorkItemParticle.WorkItemTitle = parentValue.value[0].fields.Title;
parentWorkItemParticle.WorkItemAreaPath = parentValue.value[0].fields.AreaPath;
parentWorkItemParticle.WorkItemIterationPath = parentValue.value[0].fields.IterationPath;
parentWorkItemParticle.WorkItemCreatedDate = parentValue.value[0].fields.CreatedDate;
parentWorkItemParticle.WorkItemModifiedDate = parentValue.value[0].fields.ChangedDate;

complexWorkItem.ParentWorkItem = parentWorkItemParticle;

InternalWorkItemBatch childItems = await GetAllChildWorkItemsBatch(request,, "Product Backlog Item");
//Now get the children
#region Child Segments
complexWorkItem.ChildWorkItems = new List<WorkItemPart>();
if (childItems.value != null)
for (int i = 0; i < childItems.value.Length; i++)
WorkItemPart childItemPart = new WorkItemPart();
childItemPart.WorkItemId = childItems.value[i].id.ToString();
childItemPart.WorkItemType = childItems.value[i].fields.WorkItemType;
childItemPart.WorkItemState = childItems.value[i].fields.State;
childItemPart.WorkItemTitle = childItems.value[i].fields.Title;
childItemPart.WorkItemAreaPath = childItems.value[i].fields.AreaPath;
childItemPart.WorkItemIterationPath = childItems.value[i].fields.IterationPath;
childItemPart.WorkItemCreatedDate = childItems.value[i].fields.CreatedDate;
childItemPart.WorkItemModifiedDate = childItems.value[i].fields.ChangedDate;
//Add the value



int test = 0;

return parentChildWorkItems;
catch (Exception exception)
throw exception;
//return back to the original URI
request.RequestURI = baseURI;



private async Task<InternalWorkItemBatch> GetAllChildWorkItemsBatch(RequestWithToken request, string parentIDNumber, string childWorkItemType)//, string selectedIterationPathItems)
string baseURI = request.RequestURI;
InternalWorkItemBatch workItemChildBatch = new InternalWorkItemBatch();
InternalComplexWorkItem wkItm = new InternalComplexWorkItem();

//Make sure there is data
//Retrieve the query clause -- this is for PBIs now
WiqlStatement wiqlClass = new WiqlStatement
query = GenerateWiqlQuery(parentIDNumber, childWorkItemType)
string jsonBody = JsonSerializer.Serialize(wiqlClass);

request.RequestURI = baseURI + WIQL_STRING;
request.RequestBody = jsonBody;

//Need to get the list of child IDs
string jsonString = await GetHttpResponseString(request);
int[] workItemList = JsonSerializer.Deserialize<InternalWorkItemList>(jsonString).workItems.Select(x =>;

if (workItemList.Length > 0)
//Get all the child IDS
InternalWorkItemBatchRequestBody batch = new InternalWorkItemBatchRequestBody
ids = workItemList,
fields = new string[]

//update the request to get all workitems with the childidlist
string jsonBatchBody = JsonSerializer.Serialize<InternalWorkItemBatchRequestBody>(batch);

request.RequestURI = baseURI + $@"/_apis/wit/workitemsbatch?api-version=6.0";
request.RequestBody = jsonBatchBody;
request.HasBody = true;

string expandedWorkItemListjsonString = await GetHttpResponseString(request);

workItemChildBatch = JsonSerializer.Deserialize<InternalWorkItemBatch>(expandedWorkItemListjsonString);

return workItemChildBatch;


0 Replies