Making Asynchronous HTTP Requests in Discord.py

Mar 3, 2024 ยท 3 min read

Discord bots built with the discord.py library run asynchronously by default. This allows your bot to perform multiple actions in parallel without blocking. However, making HTTP requests with the standard requests library is synchronous and will block the event loop.

This is where the aiohttp library comes in. aiohttp allows you to make HTTP requests asynchronously without blocking the bot. Here's a quick overview of how to use it properly in a Discord bot.

Installation

First, install aiohttp with pip:

pip install aiohttp

Then import it in your bot code:

import aiohttp

Creating a Session

You should create an aiohttp ClientSession when your bot starts up and close it when the bot shuts down:

import aiohttp

session = None

@bot.event
async def on_ready():
    global session
    session = aiohttp.ClientSession()
    
@bot.event
async def on_disconnect():
    await session.close()

This session can be reused for all requests.

Making Requests

To make a GET request:

async with session.get(url) as response:
    data = await response.text() # or json()

For POST, PUT, DELETE, etc. just use session.post(), session.put(), etc.

The async with syntax automatically releases the connection back to the pool when done.

Handling Exceptions

Wrap requests in try/except blocks to catch errors:

try:
    async with session.get(url) as response:
       data = await response.json()
except aiohttp.ClientConnectorError:
    print("Connection error") 

This catches network errors. Catch other exceptions like aiohttp.ContentTypeError for malformed responses.

Waiting for Requests

Since requests run in the background, you need to await them if you need the result:

data = await get_data(session)
embed = build_embed(data)
await channel.send(embed=embed)

If you don't await, the rest of the code keeps running before the request finishes.

Timeouts

To avoid a request getting stuck, use timeouts:

try:
    async with session.get(url, timeout=10) as response:
        data = await response.json()
except asyncio.TimeoutError:
    print("Timeout exceeded")

This raises a TimeoutError if the request takes > 10 seconds.

Limits

Be careful not to open too many connections at once or you may hit rate limits or performance issues. Typically you should limit concurrent requests to 5-10 per domain. Consider using a Semaphore:

sem = asyncio.Semaphore(10) 

async with sem:
    async with session.get(url) as response:
       data = await response.json() 

This limits it to 10 concurrent requests.

Following Best Practices

That covers the basics! Additionally, follow general best practices like:

  • Reuse the session for all requests
  • Use asyncio.Semaphore to limit concurrency
  • Always handle exceptions and timeouts
  • Close the session on bot shutdown
  • By properly leveraging aiohttp, you can make your Discord bots more efficient and concurrent.

    Browse by tags:

    Browse by language:

    The easiest way to do Web Scraping

    Get HTML from any page with a simple API call. We handle proxy rotation, browser identities, automatic retries, CAPTCHAs, JavaScript rendering, etc automatically for you


    Try ProxiesAPI for free

    curl "http://api.proxiesapi.com/?key=API_KEY&url=https://example.com"

    <!doctype html>
    <html>
    <head>
        <title>Example Domain</title>
        <meta charset="utf-8" />
        <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
    ...

    X

    Don't leave just yet!

    Enter your email below to claim your free API key: