Asynchronous API Calls in Python with `asyncio`

Devon Jennings
February 12, 2024

In the world of Python, where efficiency and responsiveness are paramount, leveraging asynchronous programming has become a game-changer. One of the notable features empowering asynchronous programming in Python is the `asyncio` library. In this post, we'll explore how to use the power of `asyncio` to make asynchronous API calls, delving into its advantages, disadvantages, and the underlying mechanics.

Understanding `asyncio`

How `asyncio` Works:

Under the hood, `asyncio` uses an event loop to manage and execute asynchronous tasks. The event loop is responsible for scheduling tasks, handling I/O operations, and switching between tasks efficiently. When an asynchronous function encounters an I/O operation, it yields control to the event loop, allowing other tasks to run.

 

Here is a quick illustration to showcase the idea of Asynchronous load:

[Source: https://medium.com/@tssovi/how-does-asyncio-works-f5386316b7fa]

`asyncio` is a Python library that provides a framework for writing asynchronous code using coroutines, event loops, and tasks. At its core, `asyncio` enables the execution of asynchronous functions in a non-blocking manner, allowing developers to write highly concurrent code that efficiently handles I/O-bound operations.

For more information on the ‘asyncio’ library, please follow this link.

Making Asynchronous API Calls

Let's delve into the process of making asynchronous API calls using `asyncio`. Consider the following example where we have a list of API endpoints, and we want to fetch data from each endpoint asynchronously.

import aiohttp
import asyncio

# Asynchronous function to fetch data from a given URL using aiohttp
async def fetch_data(session, url):
    # Use 'session.get()' to make an asynchronous HTTP GET request
    async with session.get(url) as response:
        # Return the JSON content of the response using 'response.json()'
        return await response.json()

# Asynchronous main function
async def main():
    # List of URLs to fetch data from
    urls = [
        'https://api.example.com/endpoint1',
        'https://api.example.com/endpoint2',
        'https://api.example.com/endpoint3'
    ]

    # Create an aiohttp ClientSession for making asynchronous HTTP requests
    async with aiohttp.ClientSession() as session:
        # Create a list of tasks, where each task is a call to 'fetch_data' with a specific URL
        tasks = [fetch_data(session, url) for url in urls]
        
        # Use 'asyncio.gather()' to run the tasks concurrently and gather their results
        results = await asyncio.gather(*tasks)

    # Print the results obtained from fetching data from each URL
    print(results)

# Run the main function using 'asyncio.run()' if the script is executed
if __name__ == "__main__":
    asyncio.run(main())

Components of the Example:

fetch_data coroutine:

  • This asynchronous coroutine is responsible for making API requests using the aiohttp library.
  • It takes a session object and a URL as parameters, performs a GET request, and returns the JSON content of the response.

main coroutine:

  • This is the main asynchronous coroutine that orchestrates the API calls.
  • It defines a list of API endpoints (urls) that we want to fetch data from concurrently.
  • It creates an aiohttp.ClientSession to manage the HTTP client session for making asynchronous requests.• It creates a list of tasks using a list comprehension, where each task corresponds to calling the fetch_data coroutine with a specific URL.
  • The asyncio.gather(*tasks) function is used to concurrently execute all the tasks and await their results.

Running the main coroutine:

  • The if name == "main": block ensures that the main coroutine is executed when the script is run.
  • The asyncio.run(main()) function runs the main coroutine, initiating the asynchronous execution.

Execution Flow:

  1. The main coroutine starts by defining a list of API endpoints (urls).
  2. An aiohttp.ClientSession is created within an async with block to manage the HTTP client session. This ensures proper resource cleanup after API calls.
  3. A list of tasks is created, where each task corresponds to fetching data from a specific API endpoint using the fetch_data coroutine.
  4. asyncio.gather(*tasks) concurrently executes all the tasks, allowing multiple API requests to be made simultaneously.
  5. The results of the API calls are collected in the results variable
  6. Finally, the results are printed

Advantages of Asynchronous API Calls with asyncio

  1. Improved Performance: By making API calls asynchronously, your program can efficiently utilize CPU resources, avoiding unnecessary waiting time. This can significantly improve the overall performance of your application, especially in scenarios with multiple concurrent API calls.
  2. Responsiveness: Asynchronous programming allows your application to remain responsive while waiting for I/O operations, such as API requests. This is crucial for creating smooth user experiences, especially in web applications.
  3. Concurrent Execution: asyncio enables the concurrent execution of asynchronous tasks. This concurrency is   achieved through cooperative multitasking, allowing your application to switch between tasks without waiting for one to complete.

Disadvantages of Asynchronous API Calls with asyncio

  1. Complexity: Asynchronous programming introduces a level of complexity, especially for developers who are new to asynchronous concepts. Debugging asynchronous code might be more challenging compared to synchronous code.
  2. Not Always Applicable: Asynchronous programming shines in scenarios with I/O-bound operations. However, for CPU-bound tasks, the benefits might be less pronounced, and the complexity introduced may not be justified.
  3. Limited Libraries: While major libraries and frameworks support asynchronous programming, not all third-party libraries may be asynchronous-friendly. This can limit your choices when building asynchronous applications.

Conclusion

`asyncio` is a powerful tool for building highly concurrent and responsive applications in Python. When it comes to making asynchronous API calls, `asyncio` provides a robust framework for harnessing the benefits of asynchronous programming. However, it's crucial to weigh the advantages against the added complexity, especially in scenarios where synchronous code might suffice.

In the ever-evolving landscape of Python development, `asyncio` stands as a testament to the language's adaptability and commitment to staying at the forefront of modern programming paradigms.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Leave a reply

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Get ready for the future.

Need more?

Do you have an idea buzzing in your head? A dream that needs a launchpad? Or maybe you're curious about how Calybre can help build your future, your business, or your impact. Whatever your reason, we're excited to hear from you!

Reach out today - let's start a coversation and uncover the possibilities.