Search code examples
evernote

Evernote API Sandbox Rate Limit Duration greater than 15 seconds


The Evernote API (Python SDK) is supposed to implement rate limiting in sandbox exactly the same as production, but the rate limit only lasts for 15 seconds.

I have a test suite that tests the rate-limit by make API calls until an exception occurs.

After waiting 1 minute, the same error, EDAMSystemException(errorCode=19, rateLimitDuration=2651, _message='DuplicateNoteLimiter'), still occurs.

Code:

from evernote.api.client import EvernoteClient
from evernote.edam.error.ttypes import EDAMSystemException
import evernote.edam.type.ttypes as Types
import time


def getClient():
    return EvernoteClient(
            token=config.dev_token,
            sandbox=True
        )

def makeNote(client):
    note = Types.Note()
    note.title = 'spam'
    content = ''
    note.content = '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'
    note.content += '<en-note>'+ content + '</en-note>'
    return client.get_note_store().createNote(note)

def test_api_limit():
    client = getClient()

    try:
        while(True):
            makeNote(client)
    except EDAMSystemException as e:
           assert e.errorCode == 19
           print 'Caught Rate Limit Exception'
           time.sleep(60)
           makeNote(client) # still raise exception
           print 'No exception occurred!' # this statement is not executed.

test_api_limit()

Solution

  • In addition to errorCode=RATE_LIMIT_REACHED, the exception includes

    message="DuplicateNoteLimiter"

    This message indicates that Evernote service has detected a large number of note duplication via API call. Since it does not make sense to have so many notes with the same content in an account, it is limiting such a request to avoid wasting their computing resources.

    If you try to create a note with unique content, you would not see this behavior.

    For example :

    you can try to include a system time in milliseconds in a new note in your code

    from datetime import datetime
    
    def makeNote(client):
        note = Types.Note()
        note.title = 'spam'
        content = str(datetime.now().microsecond)
        note.content = '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'
        note.content += '<en-note>'+ content + '</en-note>'
        return client.get_note_store().createNote(note)
    

    This modification will prevent the "duplication limiter" from getting triggered. According to my testing, the rate limit will be reset almost immediately after it is thrown. Somehow the reset happens much sooner than 15 seconds as Evernote explains.

    BTW, your code invokes client.get_note_store() every time you create a note. You should just call once to get a NoteStore handler to reuse. Since the function call will lead to an API call on UserStore, you will waste your allowance otherwise.