Skip to main content
RapidDev - Software Development Agency
replit-integrationsStandard API Integration

How to Integrate Replit with Doodle

To integrate Replit with Doodle, obtain a Doodle API key and store it in Replit Secrets (lock icon 🔒). Call the Doodle REST API from your Python or Node.js backend to create scheduling polls, retrieve participant votes, and automate meeting coordination. Deploy with Autoscale for web-triggered poll creation or Reserved VM for scheduled reminders.

What you'll learn

  • How to obtain Doodle API credentials and configure them in Replit Secrets
  • How to create Doodle scheduling polls programmatically with multiple time slots
  • How to retrieve poll results and identify the winning time option
  • How to automate meeting coordination workflows using Python and Node.js
  • How to handle Doodle webhook notifications for participant voting events
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate13 min read20 minutesProductivityMarch 2026RapidDev Engineering Team
TL;DR

To integrate Replit with Doodle, obtain a Doodle API key and store it in Replit Secrets (lock icon 🔒). Call the Doodle REST API from your Python or Node.js backend to create scheduling polls, retrieve participant votes, and automate meeting coordination. Deploy with Autoscale for web-triggered poll creation or Reserved VM for scheduled reminders.

Why Connect Replit to Doodle?

Doodle is the dominant group scheduling tool with over 30 million users, and its REST API lets developers embed meeting coordination directly into their applications. Connecting Replit to Doodle means you can create scheduling polls automatically when an event is initiated in your app, collect participant availability programmatically, and present the results without anyone leaving your interface.

The most powerful use case is automating the entire scheduling workflow: when a new project is created in your app, automatically generate a Doodle poll for the kickoff meeting, email all stakeholders a link, and trigger a calendar invite when voting is complete. Without the API, this process requires manual poll creation and monitoring. With Replit as the backend, the whole flow can run without human intervention.

Doodle's API is particularly well-suited for Replit integration because it is lightweight and stateless — your server handles the business logic while Doodle handles the scheduling interface and vote collection. Replit Secrets (lock icon 🔒 in the sidebar) keeps your credentials encrypted and out of version control. Always call the Doodle API from server-side code, never from client-side JavaScript where credentials could be exposed.

Integration method

Standard API Integration

You connect Replit to Doodle by obtaining API credentials from the Doodle developer portal, storing them in Replit Secrets, and calling the Doodle REST API from your server-side Python or Node.js code. The API supports creating polls with time options, retrieving participant responses, and automating meeting coordination workflows. Authentication uses API key headers on all requests.

Prerequisites

  • A Replit account with a Python or Node.js project created
  • A Doodle account (free account works for basic API access)
  • Doodle API credentials obtained from the Doodle developer portal or enterprise account
  • Basic familiarity with REST APIs and JSON request bodies
  • Node.js 18+ or Python 3.10+ (both available on Replit by default)

Step-by-step guide

1

Obtain Doodle API Credentials

Doodle's API is available through their developer program. To get API access, visit the Doodle API documentation at developer.doodle.com and apply for API access. For enterprise accounts, API credentials are available directly through your account settings under Integrations. For standard accounts, submit a developer API request form explaining your integration use case. Doodle will provide an API key and typically a client ID for OAuth flows if you need to act on behalf of other users' accounts. If you are building a personal automation that only accesses your own Doodle account, an API key alone is sufficient for most operations. If you need to create polls on behalf of other users (for example, in a multi-user app where each user has their own Doodle account), you will need to implement OAuth 2.0 authorization flow. For the purposes of this integration guide, we will use API key authentication which covers the most common use case: your Replit backend creating and monitoring polls from a single organizer account. Copy your API key once it is issued — you will add it to Replit Secrets in the next step.

Pro tip: If you are testing the integration before receiving production API credentials, Doodle provides a sandbox environment at sandbox.doodle.com for approved developers. Use sandbox credentials for all testing to avoid creating real polls during development.

Expected result: You have a Doodle API key (and optionally a client ID) ready to add to Replit Secrets.

2

Store Doodle Credentials in Replit Secrets

Open your Replit project and click the lock icon 🔒 in the left sidebar to open the Secrets pane. Add the following secrets using the 'Add a new secret' form: - Key: DOODLE_API_KEY — Value: your Doodle API key - Key: DOODLE_BASE_URL — Value: https://api.doodle.com (or the sandbox URL during development) If you have OAuth credentials for acting on behalf of multiple users, also add: - Key: DOODLE_CLIENT_ID — Value: your OAuth client ID - Key: DOODLE_CLIENT_SECRET — Value: your OAuth client secret Click 'Add Secret' after each entry. Replit encrypts these values and injects them as environment variables at runtime. They are never exposed in your file tree or Git history. In Python access them with os.environ['DOODLE_API_KEY']; in Node.js use process.env.DOODLE_API_KEY. Restart your Repl after adding secrets so they are picked up by the running process.

Pro tip: Create separate Replit projects (or use separate Replit Secrets per project) if you integrate with Doodle for multiple clients. This prevents credential cross-contamination and makes it easy to rotate keys independently.

Expected result: DOODLE_API_KEY and DOODLE_BASE_URL appear in the Replit Secrets pane and are accessible as environment variables in your code.

3

Create and Manage Polls with Python

The Doodle REST API lets you create polls by sending a POST request with a JSON body containing the poll title, description, time slots, and configuration options. Each time slot is defined with a start time (Unix timestamp in milliseconds) and a duration in minutes. After creating a poll, Doodle returns a poll ID and a URL that participants can use to vote. You store the poll ID in your database to retrieve results later. The GET endpoint for a poll returns all participants and their votes, letting you programmatically determine the winning time slot. The Python code below shows a complete module for creating polls, retrieving results, and identifying the best meeting time. Install requests first with pip install requests in the Replit shell.

doodle_polls.py
1import os
2import requests
3from datetime import datetime, timedelta
4from typing import Optional
5
6API_KEY = os.environ["DOODLE_API_KEY"]
7BASE_URL = os.environ.get("DOODLE_BASE_URL", "https://api.doodle.com")
8
9HEADERS = {
10 "Authorization": f"Bearer {API_KEY}",
11 "Content-Type": "application/json",
12 "Accept": "application/json"
13}
14
15def create_poll(
16 title: str,
17 description: str,
18 time_slots: list,
19 organizer_name: str,
20 organizer_email: str
21) -> dict:
22 """
23 Create a Doodle poll.
24 time_slots: list of (start_datetime, duration_minutes) tuples
25 Returns poll data including the poll URL for sharing.
26 """
27 # Convert datetime objects to millisecond timestamps
28 options = []
29 for slot_start, duration_minutes in time_slots:
30 start_ms = int(slot_start.timestamp() * 1000)
31 end_ms = start_ms + (duration_minutes * 60 * 1000)
32 options.append({"start": start_ms, "end": end_ms})
33
34 payload = {
35 "title": title,
36 "description": description,
37 "initiator": {
38 "name": organizer_name,
39 "email": organizer_email
40 },
41 "options": options,
42 "settings": {
43 "ifNeedBe": True, # Allow 'if need be' responses
44 "hidden": False, # Show all participant names to voters
45 "notifyParticipants": True
46 }
47 }
48
49 response = requests.post(
50 f"{BASE_URL}/v2/polls",
51 json=payload,
52 headers=HEADERS
53 )
54 response.raise_for_status()
55 return response.json()
56
57def get_poll(poll_id: str) -> dict:
58 """Retrieve poll details, options, and all participant votes."""
59 response = requests.get(
60 f"{BASE_URL}/v2/polls/{poll_id}",
61 headers=HEADERS
62 )
63 response.raise_for_status()
64 return response.json()
65
66def find_best_time(poll_data: dict) -> Optional[dict]:
67 """
68 Analyze poll results and return the option with most 'yes' votes.
69 Returns the winning option dict or None if no votes yet.
70 """
71 options = poll_data.get("options", [])
72 participants = poll_data.get("participants", [])
73
74 if not participants:
75 return None
76
77 vote_counts = {i: 0 for i in range(len(options))}
78 for participant in participants:
79 for vote_index, vote in enumerate(participant.get("preferences", [])):
80 if vote == 1: # 1 = yes, 0 = no, -1 = if need be
81 vote_counts[vote_index] = vote_counts.get(vote_index, 0) + 1
82
83 best_index = max(vote_counts, key=vote_counts.get)
84 if vote_counts[best_index] == 0:
85 return None
86
87 best_option = options[best_index]
88 best_option["vote_count"] = vote_counts[best_index]
89 return best_option
90
91def delete_poll(poll_id: str) -> None:
92 """Delete a poll (organizer only)."""
93 response = requests.delete(
94 f"{BASE_URL}/v2/polls/{poll_id}",
95 headers=HEADERS
96 )
97 response.raise_for_status()
98
99# Example usage
100if __name__ == "__main__":
101 now = datetime.now()
102 # Propose next Monday and Tuesday at 10am and 2pm
103 days_until_monday = (7 - now.weekday()) % 7 or 7
104 monday = now + timedelta(days=days_until_monday)
105
106 slots = [
107 (monday.replace(hour=10, minute=0, second=0), 60),
108 (monday.replace(hour=14, minute=0, second=0), 60),
109 ((monday + timedelta(days=1)).replace(hour=10, minute=0, second=0), 60),
110 ]
111
112 poll = create_poll(
113 title="Q2 Planning Meeting",
114 description="Please vote for your preferred time for our quarterly planning session.",
115 time_slots=slots,
116 organizer_name="Alex Smith",
117 organizer_email="alex@example.com"
118 )
119 print(f"Poll created: {poll.get('url')}")
120 print(f"Poll ID: {poll.get('id')}")

Pro tip: Store the poll ID in your database immediately after creation. You will need it to retrieve results, send reminders, and delete the poll when the meeting is confirmed. If you lose the poll ID you cannot recover it without listing all polls.

Expected result: Running the Python script creates a new Doodle poll with three time slot options and prints the shareable poll URL.

4

Build a Poll Management API with Node.js and Express

A Node.js Express server provides HTTP endpoints that your frontend can call to create polls on demand and check results. This is the most common Replit deployment pattern for Doodle integrations: the frontend collects the meeting details from the user, POSTs them to your Replit backend, and the backend handles the Doodle API call. The Express server below exposes POST /poll to create a poll and GET /poll/:id to check results. It also includes logic to determine the best time option. Run npm install express axios to install dependencies. For deployment, configure your .replit file to use Autoscale deployment since this server handles on-demand HTTP requests. Autoscale scales to zero when idle, making it cost-effective for applications with variable traffic.

server.js
1const express = require('express');
2const axios = require('axios');
3
4const app = express();
5app.use(express.json());
6
7const API_KEY = process.env.DOODLE_API_KEY;
8const BASE_URL = process.env.DOODLE_BASE_URL || 'https://api.doodle.com';
9
10const doodleHeaders = {
11 'Authorization': `Bearer ${API_KEY}`,
12 'Content-Type': 'application/json',
13 'Accept': 'application/json'
14};
15
16// POST /poll — create a new scheduling poll
17app.post('/poll', async (req, res) => {
18 const { title, description, timeSlots, organizerName, organizerEmail } = req.body;
19
20 if (!title || !timeSlots || timeSlots.length === 0) {
21 return res.status(400).json({ error: 'title and timeSlots are required' });
22 }
23
24 // timeSlots: [{startIso: '2026-04-07T10:00:00', durationMinutes: 60}, ...]
25 const options = timeSlots.map(slot => {
26 const startMs = new Date(slot.startIso).getTime();
27 const endMs = startMs + slot.durationMinutes * 60 * 1000;
28 return { start: startMs, end: endMs };
29 });
30
31 try {
32 const response = await axios.post(`${BASE_URL}/v2/polls`, {
33 title,
34 description: description || '',
35 initiator: { name: organizerName, email: organizerEmail },
36 options,
37 settings: { ifNeedBe: true, hidden: false, notifyParticipants: true }
38 }, { headers: doodleHeaders });
39
40 res.json({
41 pollId: response.data.id,
42 pollUrl: response.data.url,
43 message: 'Poll created successfully'
44 });
45 } catch (error) {
46 console.error('Doodle API error:', error.response?.data || error.message);
47 res.status(500).json({ error: 'Failed to create poll' });
48 }
49});
50
51// GET /poll/:id — get poll results and best time
52app.get('/poll/:id', async (req, res) => {
53 const { id } = req.params;
54 try {
55 const response = await axios.get(`${BASE_URL}/v2/polls/${id}`, {
56 headers: doodleHeaders
57 });
58 const poll = response.data;
59 const participants = poll.participants || [];
60 const options = poll.options || [];
61
62 // Count votes per option
63 const voteCounts = new Array(options.length).fill(0);
64 participants.forEach(p => {
65 (p.preferences || []).forEach((vote, i) => {
66 if (vote === 1) voteCounts[i]++;
67 });
68 });
69
70 const bestIndex = voteCounts.indexOf(Math.max(...voteCounts));
71 const bestOption = voteCounts[bestIndex] > 0 ? {
72 ...options[bestIndex],
73 voteCount: voteCounts[bestIndex]
74 } : null;
75
76 res.json({
77 title: poll.title,
78 participantCount: participants.length,
79 bestOption,
80 allOptions: options.map((opt, i) => ({ ...opt, voteCount: voteCounts[i] }))
81 });
82 } catch (error) {
83 console.error('Doodle API error:', error.response?.data || error.message);
84 res.status(500).json({ error: 'Failed to retrieve poll' });
85 }
86});
87
88app.listen(3000, '0.0.0.0', () => {
89 console.log('Doodle integration server running on port 3000');
90});

Pro tip: Add request validation middleware to check that timeSlots are in the future before creating the poll. Doodle will reject polls with time slots in the past, so catching this server-side gives your users a clearer error message.

Expected result: The Express server starts on port 3000. POST /poll creates a Doodle poll and returns the poll URL. GET /poll/:id returns participant count and the winning time option.

Common use cases

Automated Project Kickoff Scheduling

When a new project is created in your Replit app, automatically generate a Doodle poll with proposed meeting times based on team calendar availability, send invites to all project members, and mark the project as 'scheduling in progress' until the poll closes.

Replit Prompt

Build a Flask endpoint that creates a Doodle poll with five time slot options when a new project is POSTed, stores the poll ID in the database, and sends the Doodle link to all project members via email.

Copy this prompt to try it in Replit

Event Registration with Preferred Time Voting

A Replit backend creates a Doodle poll when an event is proposed, lets attendees vote for their preferred session time, then automatically finalizes the event schedule and sends confirmations once a minimum number of votes are received.

Replit Prompt

Create a Node.js backend that generates a Doodle poll for a webinar with three time options, polls the Doodle API every hour to check vote counts, and triggers a confirmation email to all voters when a winning time slot is determined.

Copy this prompt to try it in Replit

Recurring Team Meeting Reschedule Bot

When a recurring team meeting is cancelled due to a conflict, a Replit script automatically creates a new Doodle poll for the affected week, notifies team members, and updates the calendar with the new time once voting closes.

Replit Prompt

Write a Python script that detects cancelled calendar events via a webhook, creates a Doodle reschedule poll with alternative times, and sends a Slack notification with the Doodle link to the affected team channel.

Copy this prompt to try it in Replit

Troubleshooting

API returns 401 Unauthorized on all requests

Cause: The API key is missing, incorrect, or not being sent in the correct header format. Doodle expects a Bearer token in the Authorization header.

Solution: Verify DOODLE_API_KEY is set in Replit Secrets and that the header is formatted as 'Authorization: Bearer YOUR_KEY' (not 'Basic' or 'ApiKey'). Restart your Repl after adding or updating the secret to ensure it is injected into the running process.

typescript
1HEADERS = {
2 "Authorization": f"Bearer {os.environ['DOODLE_API_KEY']}",
3 "Content-Type": "application/json"
4}

Poll creation returns 400 Bad Request with 'invalid options' or 'options in the past'

Cause: The time slots passed to the Doodle API are either malformatted or have start times in the past. Doodle requires options to be in the future and timestamps must be in milliseconds (not seconds).

Solution: Ensure all time slot start times are in the future and that timestamps are in milliseconds (multiply Unix timestamp in seconds by 1000). Validate dates server-side before calling the Doodle API.

typescript
1# Correct: millisecond timestamp
2start_ms = int(slot_start.timestamp() * 1000)
3# Wrong: second timestamp
4# start_ms = int(slot_start.timestamp())

GET /v2/polls/{id} returns 404 Not Found

Cause: The poll ID is incorrect, the poll has been deleted, or you are mixing sandbox and production API endpoints. Polls created on sandbox.doodle.com are not accessible via api.doodle.com.

Solution: Verify the poll ID in your database matches the ID returned during poll creation. Ensure DOODLE_BASE_URL is set consistently — use sandbox URL for testing and production URL for live deployments. Check that the poll has not been deleted or expired.

poll.participants is empty even though participants have voted

Cause: The poll may have been created with the 'hidden' setting set to True, which hides participant names and responses from the organizer in the API response until the poll is closed.

Solution: Set 'hidden': False when creating the poll to allow organizer-level visibility of participant responses via the API. If the poll was already created with hidden=True, you must recreate it with the correct setting.

typescript
1"settings": {
2 "ifNeedBe": True,
3 "hidden": False, # Must be False to read participants via API
4 "notifyParticipants": True
5}

Best practices

  • Store the Doodle poll ID in your application database immediately after creation so you can retrieve results and manage the poll lifecycle
  • Always use future timestamps for time slots and validate dates server-side before calling the API to avoid 400 errors
  • Set 'hidden': False when creating polls if your integration needs to read participant responses programmatically
  • Use Replit Secrets for all Doodle credentials — never hardcode API keys in source files that could be committed to version control
  • Poll results in Doodle accumulate over time, so implement polling logic (or Doodle webhooks if available) to check results periodically rather than reading once
  • Delete polls after the meeting is confirmed to keep your Doodle account clean and avoid participant confusion from stale polls
  • For multi-user applications where each user has their own Doodle account, implement OAuth 2.0 to act on their behalf rather than using a single organizer API key
  • Deploy on Replit Autoscale for HTTP-triggered poll creation and use a scheduled Reserved VM job for automated poll monitoring and reminder sending

Alternatives

Frequently asked questions

How do I connect Replit to Doodle?

Obtain a Doodle API key from the Doodle developer program or your enterprise account settings, then add it as DOODLE_API_KEY in Replit Secrets (lock icon 🔒 in the sidebar). Send Bearer token authentication in the Authorization header when making requests from your Python or Node.js backend.

Does Replit work with Doodle's free account?

The Doodle free plan allows manual poll creation through the web interface. API access typically requires applying for developer access or using an enterprise account. Check the Doodle developer portal for current API access requirements, as they may vary by account tier.

How do I find the winning time slot from a Doodle poll via the API?

Retrieve the poll with GET /v2/polls/{id} and iterate over participants[].preferences, where each preference array index corresponds to an option index and a value of 1 means yes. Count votes per option index and find the maximum.

Can I automatically create Doodle polls from my Replit app?

Yes. POST to /v2/polls with a JSON body containing the title, organizer email, and an options array of start/end millisecond timestamps. The response includes the poll URL to share with participants and a poll ID to track results.

What deployment type should I use on Replit for Doodle integrations?

Use Autoscale deployment if poll creation is triggered by user actions in a web app, since it scales to zero when idle. Use Reserved VM if you need a continuously running server that monitors polls on a schedule and sends reminders or confirmations automatically.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.