Making asynchronous API calls in batches using c#
Tags: csharp, dotnet, api
Published: 2021-10-22 23:45:54

Whether you are calling an API to consume data or doing some web scraping, you may at times run into a situation where you may not want to smash their API too hard which may result in getting your IP or account blocked or just the call to fail.

This article will demonstrate how to break your call up into batches, wait a little bit and then call the next batch.

#Example
In this demo example, I have a requirement where I need to call an API 25 times but I don't want to do it all at the same time - I want to make them in batches of 10 asynchronous calls at a time and wait 5 seconds between each batch.

I've created a small console app that does this [here](https://github.com/waiholiu/Demo-MultipleRequestsWithDelay) so feel free to clone and run it.

#Prereq.

You should be able to clone the solution and press f5 to get going. I will mention I'm using a nuget package called RestSharp to make the API calls and I'm calling a website called jsonplaceholder.typicode.com which just gives me demo data.

#Craft the queries to be executed

```
var client = new RestClient("https://jsonplaceholder.typicode.com/comments");

            List<RestRequest> lstRestRequests = new List<RestRequest>();

            // this bit crafts the queries I want to call
            for (int i =0 ; i < 25; i++)
            {
                
                var request = new RestRequest(Method.GET);
                request.AddQueryParameter("postId", i.ToString());

                lstRestRequests.Add(request);
            }
```

The above code just goes through and creates the 25 queries I'd like to call and storing it into `lstRestRequests`

#Split up the list
```
            // this list stores all my results
            List<IRestResponse> finishedTasks = new List<IRestResponse>();

            // split the list of queries into batches of 30 each
            var batchCalls = splitList(lstRestRequests, 10);
```
Remember that I don't want to call all 25 at once - what I'll do is split them up into multiple lists of 10. In this example, `batchCalls` will have 3 lists - the first 2 with 10 calls and the last with 5.


```
        // Splits a list into multiple smaller lists of specified batch size
        private static IEnumerable<List<T>> splitList<T>(List<T> locations, int size = 100)
        {
            for (int i = 0; i < locations.Count; i += size)
            {
                yield return locations.GetRange(i, Math.Min(size, locations.Count - i));
            }
        }
```

This helper method does the work of breaking up the lists into smaller lists.

#Make the calls in batches
```
foreach (var batch in batchCalls)
            {
                //call each batch of 30 at the same time
                finishedTasks.AddRange(await Task.WhenAll(batch.Select(r => ExecuteRequest(r, client))));

                //after executing the current batch, wait 5 seconds
                await Task.Delay(5000);
                Console.WriteLine("taken time off, ready to go again");
            }
```

I loop through `batchCalls` and for each batch, I will make all of the calls at the same time by executing `batch.Select(r => ExecuteRequest(r, client)`

The line `finishedTasks.AddRange(await Task.WhenAll` means I will wait till all the calls are finished before adding them all to FinishedTasks for processing later.

Lastly, I have `await Task.Delay(5000);` which will wait 5 seconds before going to the next batch.

```        
// this method fires off the request
        private static Task<IRestResponse> ExecuteRequest(RestRequest r, RestClient client)
        {
            Console.WriteLine("Calling API " + r.Parameters[0]);
            return client.ExecuteAsync(r);
        }
```

As reference, the `ExecuteRequest` method is just calling the API.

#Output

When you run it the output should look like this, demonstrating that it's making the call in batches and then waiting a bit before moving onto the next batch

![Output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/arsqnharwwse4ha51eio.png)


#Utilise the result

```
            foreach (var task in finishedTasks)
            {
                Console.WriteLine(task.Content.ToString());
            }
```

Once all the batches have run, all the responses will be stored in `finishedTasks` and then it's really just a matter of looping through and doing whatever you need to do with the data