Smartsheet API in Python: Inaccessible 429 Response

I_B
I_B
edited 08/01/22 in API & Developers

I am trying to understand / plan for the rate limits, particularly for the get_cell_history method in the python api.

When I run the code below, I see the following response printed to my console:

{"response": {"statusCode": 429, "reason": "Too Many Requests", "content": {"errorCode": 4003, "message": "Rate limit exceeded.", "refId": "SOME_REF_ID"}}}

But I am tyring to force my if statement to read the response and check for the 429 status code

import smartsheet

smartsheet_client = smartsheet.Smartsheet('My_API_KEY')
smartsheet_client.errors_as_exceptions(True)

sheet_id = 'My_Sheet_ID'
edit_sheet = smartsheet_client.Sheets.get_sheet(sheet_id)  # type: smartsheet.smartsheet.models.Sheet

for row in edit_sheet.rows:  # type: smartsheet.smartsheet.models.Row
    for cell in row.cells:  # type: smartsheet.smartsheet.models.Cell
        cell_history = smartsheet_client.Cells.get_cell_history(sheet_id, row.id, cell.column_id,
                                                                include_all=True)
        if cell_history.request_response.status_code == 429:
            print(f'Rate limit exceeded.')
        elif cell_history.request_response.status_code == 200:
            print(f'Found Cell History: {len(cell_history.data)} edits')


Where can I access this response? Why does my program run without printing out the "Rate limit exceeded" string?


I am using Python 3.8 and the package smartsheet-python-sdk==2.105.1  

Best Answer

  • I_B
    I_B
    edited 08/02/22 Answer ✓

    In case anyone finds this thread, I posted this to stackoverflow and received this answer:


    Apparently in the OperationErrorResult class, there are a few errors which undergo an auto-retry:

    4003: {
        'name': 'RateLimitExceededError',
        'recommendation': ('Retry using exponential backoff. Hint: '
                           'Reduce the rate at which you are sending '
                           'requests.'),
        'should_retry': True},
    

    So the error is handled and the request is automatically retried.


    If anyone wishes for further details, please see the linked post to stackoverflow

Answers

  • Etienne Mermillod
    Etienne Mermillod ✭✭✭✭✭

    Hi,

    There are some api usage rate limitation within smartsheet, and history calls are consuming '10 calls'. Thus reaching the rate limit is quite fast with hsitory calls.

    The current limit is  300 requests per minute. Thus 30 history calls per minute. I would suggest you to add a sleep in your code to avoid the issue, or a http retrier after some sleep.

  • I understand that there is a rate limit. My concern is how the program as I've written it recognizes that it has exceeded the rate limit.

    There is a response from the API formatted like this but it is only printed to my screen:

     {"response": {"statusCode": 429, "reason": "Too Many Requests", "content": {"errorCode": 4003, "message": "Rate limit exceeded.", "refId": "SOME_REF_ID"}}}
    

    I am trying to read the statusCode as you can see in my if statement:

            if cell_history.request_response.status_code == 429:
    

    I cannot access this. If I could, I would then sleep and try to re-run. But because the condition in the if statement never satisfied, I can't run any sleep commands nor can I check if I do receive the data in the request that triggered the response printed to the console.

  • Etienne Mermillod
    Etienne Mermillod ✭✭✭✭✭

    In the past i had the same issue and did the test on the response message, such as :

    catch (Exception e) when (e.Message == "Rate limit exceeded.")

    It's c# but the idea is the same.

  • I just tried a blanket try/except in the code and I never caught the exception:

    import smartsheet
    
    smartsheet_client = smartsheet.Smartsheet('My_API_KEY')
    smartsheet_client.errors_as_exceptions(True)
    
    sheet_id = 'My_Sheet_ID'
    edit_sheet = smartsheet_client.Sheets.get_sheet(sheet_id)  # type: smartsheet.smartsheet.models.Sheet
    
    for row in edit_sheet.rows:  # type: smartsheet.smartsheet.models.Row
        for cell in row.cells:  # type: smartsheet.smartsheet.models.Cell
            try:
                cell_history = smartsheet_client.Cells.get_cell_history(sheet_id, row.id, cell.column_id,
                                                                        include_all=True)
            except:
                print('hit the exception')
            if cell_history.request_response.status_code == 429:
                print(f'Rate limit exceeded.')
            elif cell_history.request_response.status_code == 200:
                print(f'Found Cell History: {len(cell_history.data)} edits')
    
    

    But still, the message below is printed to the console:

    {"response": {"statusCode": 429, "reason": "Too Many Requests", "content": {"errorCode": 4003, "message": "Rate limit exceeded.", "refId": "SOME_REF_ID"}}}
    

    There must be some error handling within the smartsheet.Smartsheet that does something that prevents me from accessing the response.

  • Etienne Mermillod
    Etienne Mermillod ✭✭✭✭✭

    The code make sense, maybe some typo somewhere, can you debug it ?

  • There is no typo. When I debug and introspect, I see this as the state for the cell_history:

    I cannot access this when there is a rate limit error. The bad response is printed to the screen.

    for the smartsheet.Smartsheet class, I also set

    smartsheet_client.errors_as_exceptions(True)
    

    This does not raise an exception.

    I cannot access the response when there is a rate limit exceeded error

  • I_B
    I_B
    edited 08/02/22 Answer ✓

    In case anyone finds this thread, I posted this to stackoverflow and received this answer:


    Apparently in the OperationErrorResult class, there are a few errors which undergo an auto-retry:

    4003: {
        'name': 'RateLimitExceededError',
        'recommendation': ('Retry using exponential backoff. Hint: '
                           'Reduce the rate at which you are sending '
                           'requests.'),
        'should_retry': True},
    

    So the error is handled and the request is automatically retried.


    If anyone wishes for further details, please see the linked post to stackoverflow