sjkingo / python-freshdesk

An API for the Freshdesk helpdesk
BSD 2-Clause "Simplified" License
87 stars 67 forks source link

Is there a way to deal with Freshdesk rate limits on API calls? #91

Open xedus opened 7 months ago

xedus commented 7 months ago

1- Is there away to deal with Freshdesk API call rate limit? 2-Is there away to get the "requester" information by default when calling list_tickets or filter_tickets rather than call get_ticket(ticket.id, "requester") 3-How can I have access to the underlying response header?

After calling filter_tickets() I need to loop on the list of returned tickets and call requesterInfo = fd_api.tickets.get_ticket(ticket.id, "requester") in order to get the requester information. this more often than not results in the following error:

freshdesk.v2.errors.FreshdeskRateLimited: 429 Rate Limit Exceeded: API rate-limit has been reached until 9 seconds. See http://freshdesk.com/api#ratelimit

I have added a delay between calls but it does not always work. the only way to make sure the rate limit is not hit is to reduce the rate to 20 or fewer calls per minute which is too long for my case. the rate and the remaining calls are returned with every response header but I don't know how to read it in the code. This library does not seem to expose this number either.

robertvy commented 7 months ago

Have you tried using the Retry-After header from the API's response to automatically set your wait time? When you hit the rate limit and get a 429 error, this header tells you how many seconds to wait before making another request. By catching this error and using the time in the Retry-After header to pause your script (with something like time.sleep() in Python), you can manage your request rate more effectively. This way, your application can adhere to the API's limits without manual intervention. How has this worked out for you if you've tried it?

xedus commented 7 months ago

I don't know how to access the raw response header from the API object.

fd_api = API(domain, api_key) tickets = fd_api.tickets.filter_tickets(query) the following is called from within the loop: requesterInfo = fd_api.tickets.get_ticket(ticket.id, "requester")

I have added all 3 object to the debugger WATCH to see if there is a way I can access the raw response header but I can't find it.

robertvy commented 7 months ago

Yes, I am using this as a workaround with this package:

def handle_rate_limit(api_call_func, *args, **kwargs):
    '''
    Handles the rate limit exception raised by the Freshdesk API when using the Freshdesk Python SDK.
    '''
    retry = True

    while retry:
        try:
            result = api_call_func(*args, **kwargs)
            retry = False
        except FreshdeskRateLimited as e:
            # Extract the number of seconds from the exception message using a regular expression
            message = str(e)
            pattern = r"(\d+) seconds"
            match = re.search(pattern, message)
            if match:
                retry_after = int(match.group(1))
            else:
                retry_after = 60  # Default to retrying after 60 seconds if no seconds found

            print(
                f"Rate Limit Exceeded. Retrying after {retry_after} seconds...")
            time.sleep(retry_after)

    return result
xedus commented 7 months ago

oh you are parsing the error message itself. that is clever I might just do that.

previously I was trying to access the raw HTTP response looking for this portion:

HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 X-Ratelimit-Total: 700 X-Ratelimit-Remaining: 426 X-Ratelimit-Used-CurrentRequest: 1 X-Freshdesk-Api-Version: latest=v2; requested=v2

this is from their documentation page. if you know how to do this please let me know. in the meantime I will copy your solution and parse the error message to get the time.

robertvy commented 7 months ago

yes I think I used that workaround, after realizing the underlying response may be hard to get to without forking the package. For requests, which are not covered by this wrapper, I am using this:

def handle_rate_limit_request(api_call_func, *args, **kwargs):
    """
    Handles the rate limit exception raised by the Freshdesk API when using the requests library.
    """
    retry = True

    while retry:
        response = api_call_func(*args, **kwargs)

        if response.status_code == 429:
            retry_after = int(response.headers.get("Retry-After"))

            print(
                f"Rate Limit Exceeded. Retrying after {retry_after} seconds...")
            time.sleep(retry_after)
        else:
            retry = False

    return response