In this post I’m going to go over the send message to Discord webhook function from last time and add in some error handling for when we hit a rate limit. For reference, here is the code we wrote by the end of the last tutorial. This code defines the function send that will send message to the designated webhook.
import http.client def send(message, webhook): conn = http.client.HTTPSConnection("discordapp.com") payload = "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"content\"\r\n\r\n" + message + "\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--" headers = { 'content-type': "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW", 'cache-control': "no-cache", } conn.request("POST", webhook, payload, headers) res = conn.getresponse() data = res.read() print(data.decode("utf-8"))
Discord contains limits to how often a webhook can be used, these limits are updated dynamically but in general if you try to send more than 5 messages within 1-2 seconds you will probably hit a rate limit. Let’s try hitting a rate limit and see what happens. Put the following code into your client of choice after defining the send function above:
for i in range(0,7): send("further reading", YOURWEBHOOK)
You will eventually end up with one or two responses like below, but note that the ordering may be different:
{ "global": false, "message": "You are being rate limited.", "retry_after": 689 }
That is the message that Discord gives you when rate limited. It will start happening after about the fifth message sent in too quick a succession. You will also notice that any rate limited message did not get posted to the channel. Each rate limit will include the phrase “You are being rate limited.” and will give a number is milliseconds to wait before trying again.
To properly handle this issue, we will write an if statement that looks for a response from discord about the rate limit and retries after waiting an appropriate amount of time. You will find an example below with some commenting explaining each step:
import http.client #import the time module for later import time def send(message, webhook): conn = http.client.HTTPSConnection("discordapp.com") payload = "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"content\"\r\n\r\n" + message + "\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--" headers = { 'content-type': "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW", 'cache-control': "no-cache", } conn.request("POST", webhook, payload, headers) res = conn.getresponse() data = res.read() response = data.decode("utf-8") #checks if there is a response if response: #check if it's a rate limit if "You are being rate limited" in response: #find the wait time, it will be on the same line as "retry after" index1 = response.find('"retry_after"') wait = '' #we are going to go through the string starting after "retry_after" and look for digits for character in response[index1 + 15:]: #we do '+ 15' to skip the start of the line and just grab the digits after if character.isdigit(): wait += character elif character == '\n': #ordering may be different, so we will set it to stop when the new line character is hit break #turn wait into a number wait = int(wait) #wait is milliseconds, so must convert to seconds #we will also add 0.1 seconds to it just to ensure we are over the wait time wait = wait/1000 + 0.1 #now we will wait that amount of time and try sending the message again print("waiting " + str(wait) + " seconds") time.sleep(wait) send(message, webhook) else: #print the response if its something else print(data.decode("utf-8"))
So in the above code, what is happening is it checks for a response from discord. If there is a response, it checks if it is reporting a rate limit. If it is a rate limit, then it looks for the “retry after” line in the string and grabs the digits that appears after it. It converts the digits from a string to an int to get milliseconds and then divides by 1000 to turn it into seconds. It will add 0.1 to ensure that we are waiting long enough, then it’ll wait before trying to send the message again.
Try running the loop again and this time you will get at least one console message informing you of a wait and all 7 messages will eventually be sent to the channel.
Being able to handle a rate limit is important, but what would be better is to avoid the rate limit entirely. While this may not be possible in some implementations, in the case of a loop like we’ve been testing with there is a way to check how close you are to the rate limit. In the next tutorial we will go over this scenario.