To integrate Replit with Evernote, complete the OAuth 1.0a authorization flow to obtain an access token, store it in Replit Secrets (lock icon π), and use the Evernote Thrift SDK or REST API to read and write notes, notebooks, and tags in ENML format from your Python or Node.js server.
Why Integrate Evernote with Replit?
Evernote holds years of personal and professional knowledge for millions of users β meeting notes, research clippings, project documentation, and reference material. Connecting Replit to the Evernote API lets you build automations that push content into Evernote from other tools, sync notes with external systems, extract structured data from note collections, or build custom search interfaces on top of a user's Evernote library.
Evernote's API differs from most modern REST APIs in an important way: it uses Apache Thrift as its protocol, which means the Python and Node.js SDKs use generated Thrift client code rather than simple HTTP calls. The Evernote SDK handles the protocol details, but you need to understand the data model: Notes contain ENML (Evernote Markup Language) content β an XML-based format derived from XHTML. When creating or updating notes, your ENML must be well-formed XML that conforms to the Evernote DTD, otherwise the API will reject the note. The SDK provides utilities for constructing valid ENML.
Authentication uses OAuth 1.0a, which is older than OAuth 2.0 but still widely used in the Evernote ecosystem. The flow requires a three-step handshake: request token β user authorization β access token. Because this involves a browser redirect, you will implement a small OAuth callback endpoint in your Replit server during the setup phase, then store the resulting access token in Replit Secrets for future server-to-server calls. Evernote also provides a Sandbox environment (sandbox.evernote.com) for development and testing before using the production API.
Integration method
Replit connects to Evernote via the Evernote API using OAuth 1.0a for authentication. After completing the OAuth flow, your access token is stored in Replit Secrets and used by your Express or Flask server to query notes, create new notes in ENML format, manage notebooks, and search content. Evernote uses a Thrift-based protocol, so most developers use the official SDK wrappers rather than raw HTTP requests.
Prerequisites
- An Evernote account (personal or business)
- A registered Evernote API application from dev.evernote.com (both Sandbox and Production keys)
- A Replit account with a Node.js or Python Repl created
- Basic understanding of OAuth authentication flows and REST API concepts
- Familiarity with Express (Node.js) or Flask (Python)
Step-by-step guide
Register your app in the Evernote Developer Portal
Register your app in the Evernote Developer Portal
Go to dev.evernote.com and sign in with your Evernote account. Navigate to 'Get an API Key' and fill out the application registration form. You will need to provide your application name, description, and what permissions it needs (Basic β read notes, or Full Access β read and write notes, notebooks, and tags). For most automation use cases, request Full Access. After submitting, Evernote provides you with a Consumer Key and Consumer Secret for the Sandbox environment. The Sandbox environment (sandbox.evernote.com) is a separate Evernote service for development β you will need a separate Sandbox account at sandbox.evernote.com to test your integration. To access the Production Evernote API, you must request activation from Evernote after demonstrating your app works in Sandbox. Keep your Consumer Key and Consumer Secret safe β these are the app-level credentials, separate from the per-user OAuth access tokens. Store both in Replit Secrets now as EVERNOTE_CONSUMER_KEY and EVERNOTE_CONSUMER_SECRET, along with a flag EVERNOTE_SANDBOX set to 'true' during development.
1# Python β install Evernote SDK2# Run in Replit Shell:3# pip install evernote34# Or add to requirements.txt:5# evernote3>=1.28.067# For Node.js, run in Replit Shell:8# npm install evernote910# Verify installed:11import pkg_resources12try:13 pkg_resources.get_distribution('evernote3')14 print('Evernote SDK installed successfully')15except pkg_resources.DistributionNotFound:16 print('Run: pip install evernote3')Pro tip: Evernote's Sandbox and Production environments are completely separate services with separate accounts. Create a free Sandbox account at sandbox.evernote.com specifically for testing β your production notes are never affected.
Expected result: Your app is registered in the Evernote Developer portal and you have Consumer Key and Consumer Secret values. EVERNOTE_CONSUMER_KEY, EVERNOTE_CONSUMER_SECRET, and EVERNOTE_SANDBOX are added to Replit Secrets.
Complete the OAuth 1.0a authorization flow
Complete the OAuth 1.0a authorization flow
Evernote's OAuth 1.0a flow requires three steps that cannot be skipped: first, your server requests a temporary token from Evernote using your Consumer Key and Secret; second, the user is redirected to Evernote's authorization page to grant access; third, Evernote redirects back to your callback URL with a verifier code, which your server exchanges for a permanent access token. To implement this in Replit, add two routes to your server: /oauth/start (which fetches the request token and redirects the user to Evernote) and /oauth/callback (which handles the redirect back and exchanges the verifier for an access token). When the access token is obtained, print it to the console so you can copy it into Replit Secrets. You will only need to run this OAuth flow once per Evernote user account β the resulting access token does not expire under normal circumstances. Set your OAuth callback URL to your Replit preview URL + /oauth/callback during development, and update it to the production deployment URL later.
1# Python β Evernote OAuth 1.0a flow (oauth_setup.py)2from flask import Flask, request, redirect3import os4from evernote.api.client import EvernoteClient56app = Flask(__name__)78CONSUMER_KEY = os.environ['EVERNOTE_CONSUMER_KEY']9CONSUMER_SECRET = os.environ['EVERNOTE_CONSUMER_SECRET']10SANDBOX = os.environ.get('EVERNOTE_SANDBOX', 'true').lower() == 'true'1112# Replace with your Replit preview or deployment URL13CALLBACK_URL = os.environ.get('REPLIT_URL', 'http://localhost:3000') + '/oauth/callback'1415client = EvernoteClient(consumer_key=CONSUMER_KEY, consumer_secret=CONSUMER_SECRET, sandbox=SANDBOX)1617@app.route('/oauth/start')18def oauth_start():19 request_token = client.get_request_token(CALLBACK_URL)20 # Store oauth_token temporarily (in production use a session or DB)21 app.config['REQUEST_TOKEN'] = request_token22 authorize_url = client.get_authorize_url(request_token)23 return redirect(authorize_url)2425@app.route('/oauth/callback')26def oauth_callback():27 oauth_token = request.args.get('oauth_token')28 oauth_verifier = request.args.get('oauth_verifier')29 request_token = app.config.get('REQUEST_TOKEN', {})30 access_token = client.get_access_token(31 request_token.get('oauth_token'),32 request_token.get('oauth_token_secret'),33 oauth_verifier34 )35 # COPY THIS TOKEN TO REPLIT SECRETS AS EVERNOTE_ACCESS_TOKEN36 print(f"ACCESS TOKEN: {access_token}")37 return f"Access token obtained. Copy it to Replit Secrets: {access_token}"3839if __name__ == '__main__':40 app.run(host='0.0.0.0', port=3000)Pro tip: Once you have the access token, you can remove the OAuth routes from your main application. The access token is long-lived and only needs to be refreshed if the user explicitly revokes your app's access in Evernote settings.
Expected result: After visiting /oauth/start and authorizing in Evernote, the callback page displays your access token. You copy it to Replit Secrets as EVERNOTE_ACCESS_TOKEN.
Store credentials in Replit Secrets
Store credentials in Replit Secrets
Open the Replit Secrets panel (lock icon π in the sidebar) and ensure you have the following secrets configured: EVERNOTE_CONSUMER_KEY (your app's consumer key from the developer portal), EVERNOTE_CONSUMER_SECRET (your app's consumer secret), EVERNOTE_ACCESS_TOKEN (the per-user access token obtained from the OAuth flow), and EVERNOTE_SANDBOX (set to 'true' for development, 'false' for production). The access token is the most sensitive value β anyone with it can read and modify the authorized user's Evernote notes. Replit's Secret Scanner automatically flags tokens that match known patterns in code commits, but the best protection is to never let the token appear in source files at all. After the OAuth setup phase, remove the OAuth routes from your app and replace them with API calls that use the stored access token directly. Install any remaining dependencies via the Replit Packages panel or Shell commands.
1// Node.js β verify Evernote secrets (verify-secrets.js)2const required = ['EVERNOTE_CONSUMER_KEY', 'EVERNOTE_CONSUMER_SECRET', 'EVERNOTE_ACCESS_TOKEN'];3for (const key of required) {4 const val = process.env[key];5 if (!val) {6 console.error(`MISSING: ${key} β add it in Replit Secrets (lock icon π)`);7 process.exit(1);8 }9 console.log(`OK: ${key} loaded (${val.length} chars)`);10}11console.log('All Evernote secrets verified.');Pro tip: Set EVERNOTE_SANDBOX=false only when you are ready to access your real Evernote data. Production API activation requires approval from Evernote β confirm your app is approved before switching.
Expected result: All four Evernote secrets appear in the Replit Secrets panel. The verification script confirms they are all loaded and non-empty.
Read notebooks and create notes with ENML
Read notebooks and create notes with ENML
With the access token stored, you can now make authenticated API calls. The Evernote SDK provides a NoteStore client that wraps all note and notebook operations. To list notebooks, call noteStore.listNotebooks(). To create a note, construct a Note object with a title, content (ENML string), and optional notebook GUID. The ENML format is critical: every note content must start with the ENML DOCTYPE declaration and use only tags allowed by the Evernote DTD. Plain text content must be wrapped in ENML paragraph tags. If your ENML is malformed, the API returns an EDAMUserException with a clear error message indicating which validation rule failed. For notes that contain only plain text or simple HTML, use the note content template shown in the code example β it handles the boilerplate and escapes text content correctly. To read an existing note's content, call noteStore.getNoteContent(noteGuid), which returns the raw ENML string. Use a standard XML parser to extract text if you need plain-text output.
1# Python β read notebooks and create a note (evernote_client.py)2import os3from evernote.api.client import EvernoteClient4from evernote.edam.type.ttypes import Note56ACCESS_TOKEN = os.environ['EVERNOTE_ACCESS_TOKEN']7SANDBOX = os.environ.get('EVERNOTE_SANDBOX', 'true').lower() == 'true'89client = EvernoteClient(token=ACCESS_TOKEN, sandbox=SANDBOX)10note_store = client.get_note_store()1112# List all notebooks13notebooks = note_store.listNotebooks()14print("Your notebooks:")15for nb in notebooks:16 print(f" {nb.name} (GUID: {nb.guid})")1718# Create a simple note19def create_note(title, plain_text, notebook_guid=None):20 enml_content = (21 '<?xml version="1.0" encoding="UTF-8"?>'22 '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'23 f'<en-note><p>{plain_text}</p></en-note>'24 )25 note = Note()26 note.title = title27 note.content = enml_content28 if notebook_guid:29 note.notebookGuid = notebook_guid30 created = note_store.createNote(note)31 print(f"Created note: {created.title} (GUID: {created.guid})")32 return created3334# Create a test note in the first available notebook35if notebooks:36 create_note("Replit Test Note", "This note was created from Replit!", notebooks[0].guid)Pro tip: Always validate ENML before submitting to the API during development. A common mistake is including unsupported HTML tags (like <div> or <style>) β only tags listed in the Evernote ENML DTD are accepted.
Expected result: The script lists your Evernote notebooks with their GUIDs and creates a new test note. The note appears in your Evernote app within a few seconds.
Search notes and build a search API endpoint
Search notes and build a search API endpoint
Evernote's search API uses the Evernote Query Language (ENL), a structured search syntax that supports keywords, notebook filters, tag filters, and date ranges. The NoteStore.findNotes() method accepts a NoteFilter object that specifies the search query and optional sorting, plus a ResultSpec that defines which note fields to return. Because note content can be large, you should set includeContent=False in the ResultSpec for list operations and only fetch full content for individual notes with getNoteContent(). Build an Express or Flask endpoint that wraps this search logic and returns results as JSON. This endpoint can power custom search interfaces, AI document retrieval pipelines, or cross-app search features. Deployed as an Autoscale Replit deployment, it provides a personal Evernote search API that you can query from any application. For production workloads, add caching (e.g., a simple in-memory dict with a TTL) to avoid hitting Evernote rate limits on repeated identical searches.
1// Node.js β Evernote search endpoint (search.js)2const Evernote = require('evernote');3const express = require('express');45const app = express();6app.use(express.json());78const ACCESS_TOKEN = process.env.EVERNOTE_ACCESS_TOKEN;9const SANDBOX = process.env.EVERNOTE_SANDBOX === 'true';1011const client = new Evernote.Client({ token: ACCESS_TOKEN, sandbox: SANDBOX });1213app.get('/search', async (req, res) => {14 const query = req.query.q;15 if (!query) return res.status(400).json({ error: 'q parameter required' });16 try {17 const noteStore = await client.getNoteStore();18 const filter = new Evernote.Types.NoteFilter({ words: query });19 const spec = new Evernote.NoteStore.NotesMetadataResultSpec({20 includeTitle: true,21 includeNotebookGuid: true,22 includeCreated: true23 });24 const result = await noteStore.findNotesMetadata(filter, 0, 10, spec);25 const notes = result.notes.map(n => ({26 guid: n.guid,27 title: n.title,28 notebook: n.notebookGuid,29 created: new Date(n.created).toISOString()30 }));31 res.json({ query, count: notes.length, notes });32 } catch (err) {33 console.error('Evernote search error:', err);34 res.status(500).json({ error: err.message });35 }36});3738app.listen(3000, '0.0.0.0', () => console.log('Evernote search API running'));Pro tip: Evernote limits API calls to 60 requests per minute for most operations. If you are building a search feature with multiple searches per user session, implement client-side debouncing to reduce API call frequency.
Expected result: A GET to /search?q=your+keyword returns a JSON array of matching notes with titles, GUIDs, and creation dates. The search API is functional and returns results from your Evernote account.
Common use cases
Automated Research Note Archiver
When your app discovers relevant web content (via RSS feeds, social monitoring, or manual curation), it automatically creates a new Evernote note in a designated research notebook with the article title, source URL, and a summary as the note content. This builds a searchable personal research archive without manual copy-pasting.
Build a Flask endpoint that accepts a JSON payload with article title, URL, and summary text, formats the content as valid ENML, and creates a new Evernote note in a 'Research' notebook. Store EVERNOTE_ACCESS_TOKEN and EVERNOTE_NOTEBOOK_GUID in Replit Secrets.
Copy this prompt to try it in Replit
Daily Summary Note Generator
A scheduled Replit job runs at end of day, queries your other APIs (task managers, calendars, project trackers) to compile a daily summary, and creates a new Evernote note in a 'Daily Log' notebook with completed tasks, meetings attended, and key decisions made. This creates a searchable daily work log automatically.
Build a Python script that generates a daily summary by combining hardcoded placeholder data (replace with your APIs), formats it as ENML with headings and bullet lists, and uses the Evernote SDK to create a dated note in a specific notebook. Schedule it to run daily using Replit's scheduled deployments.
Copy this prompt to try it in Replit
Note Search and Export API
Expose your Evernote library as a searchable API for your own applications. When users search your app, the Replit backend queries Evernote using the NoteStore search API, retrieves matching notes, strips the ENML markup to return plain text, and sends the results back as JSON. This is useful for building custom note-reading interfaces or AI Q&A tools that use your notes as a knowledge base.
Build an Express server with a GET /search endpoint that accepts a query parameter, uses the Evernote SDK to search notes with that query, fetches the content of the top 5 matches, strips ENML tags to extract plain text, and returns the results as a JSON array with title, content preview, and notebook name.
Copy this prompt to try it in Replit
Troubleshooting
EDAMUserException: BAD_DATA_FORMAT on note creation
Cause: The ENML content is malformed β it either does not include the required DOCTYPE declaration, uses HTML tags not permitted in ENML, or contains unescaped special characters like < or & in text content.
Solution: Ensure every note content string begins with the ENML DOCTYPE declaration. Replace & with & and < with < in any user-provided text before inserting it into ENML tags. Only use HTML elements listed in the Evernote ENML DTD (en-note, p, br, b, i, a, ul, li, etc.) β remove any div, span, or style tags.
1# Escape text for ENML2def enml_escape(text):3 return text.replace('&', '&').replace('<', '<').replace('>', '>')45def make_enml(body_text):6 safe = enml_escape(body_text)7 return ('<?xml version="1.0" encoding="UTF-8"?>'8 '<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'9 f'<en-note><p>{safe}</p></en-note>')OAuth flow returns 'invalid oauth_verifier' or callback URL mismatch error
Cause: The callback URL used when requesting the token does not match the one registered in the Evernote Developer portal, or the oauth_verifier query parameter was not correctly passed to the access token exchange call.
Solution: Ensure the callback URL passed to get_request_token() exactly matches a URL registered in your Evernote app settings. Check that your /oauth/callback route reads oauth_verifier from the query string correctly. In development, use your Replit preview URL (visible in the webview address bar).
All API calls return EDAMSystemException: RATE_LIMIT_REACHED
Cause: You have exceeded Evernote's API rate limit for your access token or application. Evernote enforces hourly and daily rate limits.
Solution: Add exponential backoff retry logic for API calls. Cache frequently accessed data (like notebook lists) in memory and refresh only periodically. Avoid making API calls in tight loops β batch operations where possible.
1import time2def call_with_retry(fn, *args, max_retries=3):3 for attempt in range(max_retries):4 try:5 return fn(*args)6 except Exception as e:7 if 'RATE_LIMIT' in str(e) and attempt < max_retries - 1:8 time.sleep(2 ** attempt)9 else:10 raiseNote content is fetched but shows raw ENML XML instead of plain text
Cause: The getNoteContent API returns raw ENML XML. Your code is returning this directly instead of parsing out the text content.
Solution: Parse the ENML with an XML library (xml.etree.ElementTree in Python, or a DOMParser in Node.js) and extract text nodes. Strip all XML tags to get plain text, or use a regex to remove ENML markup for simple cases.
1import xml.etree.ElementTree as ET2def enml_to_text(enml):3 # Remove DOCTYPE declaration which confuses parsers4 content = enml.split('<en-note')[1] if '<en-note' in enml else enml5 content = '<en-note' + content6 root = ET.fromstring(content)7 return ' '.join(root.itertext())Best practices
- Always complete the OAuth 1.0a flow in Sandbox first, then request Production activation from Evernote β never test with production credentials
- Store EVERNOTE_ACCESS_TOKEN, EVERNOTE_CONSUMER_KEY, and EVERNOTE_CONSUMER_SECRET exclusively in Replit Secrets (lock icon π), never in source files
- Cache notebook GUIDs in your application after the first API call β they rarely change and fetching them on every request wastes rate limit quota
- Validate ENML content before submitting to the API to catch formatting errors before they cause EDAMUserException failures
- Request only the note fields you need in ResultSpec β setting includeContent=True for large note lists significantly increases response size and latency
- Implement rate-limit-aware retry logic with exponential backoff since Evernote enforces strict hourly API call limits
- Use the Sandbox environment (EVERNOTE_SANDBOX=true) during all development and testing β switch to production only after your app is approved and verified
- Log API errors with the full EDAMUserException or EDAMSystemException details since they contain actionable error codes and messages
Alternatives
Notion offers a modern REST API with structured databases and a straightforward OAuth 2.0 flow, making it easier to integrate than Evernote's Thrift-based API with OAuth 1.0a.
Quip (Salesforce) provides collaborative documents and spreadsheets with a simpler REST API, and is a better choice if your team uses Salesforce and needs document collaboration rather than personal note sync.
Todoist's REST API v2 is significantly simpler to integrate than Evernote's Thrift API, making it a better starting point if your primary use case is task tracking rather than rich note storage.
Trello provides a simple REST API for card and board management with straightforward API key auth, suitable if you need lightweight structured content management rather than rich note storage.
Frequently asked questions
How do I connect Replit to Evernote?
Register an app at dev.evernote.com, complete the OAuth 1.0a authorization flow by running a temporary Flask or Express server on Replit, copy the resulting access token to Replit Secrets (lock icon π), and use the Evernote SDK (evernote3 for Python, evernote for Node.js) to make API calls from your server-side code.
Does Replit work with Evernote for free?
Evernote provides API access for free during development using their Sandbox environment. Production API access requires approval from Evernote's developer program. Replit's free tier is sufficient for development and testing, but you will need a paid Replit plan for always-on Autoscale deployments.
Why is OAuth 1.0a required for Evernote instead of OAuth 2.0?
Evernote has used OAuth 1.0a since the API was first released and has not migrated to OAuth 2.0. This means you need to handle request token signing and the three-step handshake rather than the simpler OAuth 2.0 code flow. Using the official Evernote SDK handles the signing details automatically.
What is ENML and why does Evernote require it?
ENML (Evernote Markup Language) is an XML-based format derived from XHTML that Evernote uses to store note content. It is required because it allows Evernote to render notes consistently across all platforms while supporting rich formatting, attachments, and media. Your API calls must produce valid ENML β malformed content will be rejected with a BAD_DATA_FORMAT error.
How do I store the Evernote access token in Replit?
After completing the OAuth flow, copy the access token string and add it to Replit Secrets as EVERNOTE_ACCESS_TOKEN via the lock icon π in the sidebar. Access it in Python with os.environ['EVERNOTE_ACCESS_TOKEN'] or in Node.js with process.env.EVERNOTE_ACCESS_TOKEN. Never store it in your source code.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation