TL;DR
The Make (formerly Integromat) API lets you automate workflows, manage scenarios, and execute integrations programmatically. It supports OAuth 2.0 and API key authentication. The RESTful endpoints allow for scenario management, execution, webhooks, and team operations, with rate limits from 60-600 requests/min (by plan). This actionable guide covers authentication, scenario CRUD, webhooks, execution monitoring, and production-ready automation.
Introduction
Make (Integromat) processes over 2 billion operations monthly for 1+ million users in 100+ countries. If you're building automation tools, managing client workflows, or integrating with 1000+ apps, Make API integration is essential for scalable automation.
Agencies managing 50+ client automations often lose 15-25 hours weekly on manual scenario updates, execution monitoring, and client reporting. API integration automates deployment, execution tracking, error handling, and reporting.
This guide is a step-by-step walkthrough of Make API integration: authentication (OAuth & API key), scenario management, webhooks, monitoring, team management, and deployment strategies. You'll be able to implement a production-ready Make integration.
💡 Apidog simplifies API integration testing: Test Make endpoints, validate OAuth, inspect execution responses, and debug automations in one workspace. Import API specs, mock responses, and share tests with your team.
What Is the Make API?
The Make API enables programmatic management of automation workflows.
Core Capabilities:
- Create, update, delete scenarios
- Manually trigger scenario execution
- Access execution history/logs
- Manage webhooks
- Manage teams, users, connections, and apps
- Adjust organization/workspace settings
Key Features
| Feature | Description |
|---|---|
| RESTful API | JSON-based endpoints |
| OAuth 2.0 + API Keys | Flexible authentication |
| Webhooks | Real-time execution notifications |
| Rate Limiting | 60-600 requests/min by plan |
| Scenario Management | Full CRUD operations |
| Execution Control | Start, stop, monitor runs |
| Team API | User and permission management |
Make Plans and API Access
| Plan | API Access | Rate Limit | Best For |
|---|---|---|---|
| Free | Limited | 60/min | Testing, learning |
| Core | Full API | 120/min | Small businesses |
| Pro | Full API + Priority | 300/min | Growing teams |
| Teams | Full API + Admin | 600/min | Agencies, enterprises |
| Enterprise | Custom limits | Custom | Large organizations |
API Architecture Overview
Base URL for v2:
https://api.make.com/api/v2/
API Versions
| Version | Status | Use Case |
|---|---|---|
| v2 | Current | All new integrations |
| v1 | Deprecated | Legacy integrations (migrate) |
Getting Started: Authentication Setup
Step 1: Create Make Account
- Go to Make.com
- Sign up for an account
- Navigate to Settings > Developer settings
- Generate API credentials
Step 2: Choose Authentication Method
| Method | Best For | Security Level |
|---|---|---|
| API Key | Internal scripts | High (store securely) |
| OAuth 2.0 | Multi-tenant/client apps | Higher (user-scoped) |
Step 3: Get API Key (Simplest Method)
Generate an API key for internal use:
- Go to Settings > Developer settings
- Click Create API key
- Copy and store securely
# .env file
MAKE_API_KEY="your_api_key_here"
MAKE_ORGANIZATION_ID="your_org_id"
Step 4: Set Up OAuth 2.0 (For Multi-Tenant Apps)
Configure OAuth for client integrations:
- Go to Settings > Developer settings > OAuth apps
- Click Create OAuth app
- Set redirect URI
- Get client credentials
const MAKE_CLIENT_ID = process.env.MAKE_CLIENT_ID;
const MAKE_CLIENT_SECRET = process.env.MAKE_CLIENT_SECRET;
const MAKE_REDIRECT_URI = process.env.MAKE_REDIRECT_URI;
// Build authorization URL
const getAuthUrl = (state) => {
const params = new URLSearchParams({
client_id: MAKE_CLIENT_ID,
redirect_uri: MAKE_REDIRECT_URI,
scope: 'read write execute',
state: state,
response_type: 'code'
});
return `https://www.make.com/oauth/authorize?${params.toString()}`;
};
Step 5: Exchange Code for Access Token
Handle the OAuth callback:
const exchangeCodeForToken = async (code) => {
const response = await fetch('https://www.make.com/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: MAKE_CLIENT_ID,
client_secret: MAKE_CLIENT_SECRET,
redirect_uri: MAKE_REDIRECT_URI,
code: code
})
});
const data = await response.json();
return {
accessToken: data.access_token,
refreshToken: data.refresh_token,
expiresIn: data.expires_in
};
};
// Handle callback
app.get('/oauth/callback', async (req, res) => {
const { code, state } = req.query;
try {
const tokens = await exchangeCodeForToken(code);
// Store tokens securely
await db.integrations.create({
userId: req.session.userId,
accessToken: tokens.accessToken,
refreshToken: tokens.refreshToken,
tokenExpiry: Date.now() + (tokens.expiresIn * 1000)
});
res.redirect('/success');
} catch (error) {
console.error('OAuth error:', error);
res.status(500).send('Authentication failed');
}
});
Step 6: Make Authenticated API Calls
Create a reusable API client:
const MAKE_BASE_URL = 'https://api.make.com/api/v2';
const makeRequest = async (endpoint, options = {}) => {
const apiKey = options.useOAuth ? await getOAuthToken() : process.env.MAKE_API_KEY;
const response = await fetch(`${MAKE_BASE_URL}${endpoint}`, {
...options,
headers: {
'Authorization': `Token ${apiKey}`,
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Make API Error: ${error.message}`);
}
return response.json();
};
// Usage
const scenarios = await makeRequest('/scenarios');
console.log(`Found ${scenarios.data.length} scenarios`);
Scenario Management
Listing Scenarios
Fetch scenarios (with filters):
const listScenarios = async (filters = {}) => {
const params = new URLSearchParams({
limit: filters.limit || 50,
offset: filters.offset || 0
});
if (filters.folder) {
params.append('folder', filters.folder);
}
const response = await makeRequest(`/scenarios?${params.toString()}`);
return response;
};
// Usage
const scenarios = await listScenarios({ limit: 100 });
scenarios.data.forEach(scenario => {
console.log(`${scenario.name} - ${scenario.active ? 'Active' : 'Paused'}`);
console.log(` Last run: ${scenario.lastRunDate || 'Never'}`);
});
Getting Scenario Details
Fetch a single scenario:
const getScenario = async (scenarioId) => {
const response = await makeRequest(`/scenarios/${scenarioId}`);
return response;
};
// Usage
const scenario = await getScenario('12345');
console.log(`Name: ${scenario.name}`);
console.log(`Modules: ${scenario.modules.length}`);
console.log(`Schedule: ${scenario.schedule?.cronExpression || 'Manual'}`);
Creating a Scenario
Create a new scenario from a blueprint:
const createScenario = async (scenarioData) => {
const scenario = {
name: scenarioData.name,
blueprint: scenarioData.blueprint, // Scenario JSON blueprint
active: scenarioData.active || false,
priority: scenarioData.priority || 1,
maxErrors: scenarioData.maxErrors || 3,
autoCommit: scenarioData.autoCommit || true,
description: scenarioData.description || ''
};
const response = await makeRequest('/scenarios', {
method: 'POST',
body: JSON.stringify(scenario)
});
return response;
};
// Usage
const newScenario = await createScenario({
name: 'Lead Sync to CRM',
blueprint: {
modules: [
{
id: 1,
app: 'webhooks',
action: 'customWebhook',
parameters: { /* ... */ }
},
{
id: 2,
app: 'salesforce',
action: 'createRecord',
parameters: { /* ... */ }
}
],
connections: [
{ from: 1, to: 2 }
]
},
active: true,
description: 'Sync webhook leads to Salesforce'
});
console.log(`Scenario created: ${newScenario.id}`);
Updating a Scenario
Modify scenario configuration:
const updateScenario = async (scenarioId, updates) => {
const response = await makeRequest(`/scenarios/${scenarioId}`, {
method: 'PATCH',
body: JSON.stringify(updates)
});
return response;
};
// Usage - Pause scenario
await updateScenario('12345', { active: false });
// Usage - Update schedule
await updateScenario('12345', {
schedule: {
cronExpression: '0 */6 * * *', // Every 6 hours
timezone: 'America/New_York'
}
});
Deleting a Scenario
Delete a scenario:
const deleteScenario = async (scenarioId) => {
await makeRequest(`/scenarios/${scenarioId}`, {
method: 'DELETE'
});
console.log(`Scenario ${scenarioId} deleted`);
};
Execution Management
Triggering Scenario Execution
Manually run a scenario:
const executeScenario = async (scenarioId, inputData = null) => {
const response = await makeRequest(`/scenarios/${scenarioId}/execute`, {
method: 'POST',
body: inputData ? JSON.stringify(inputData) : undefined
});
return response;
};
// Usage - Run without input
const execution = await executeScenario('12345');
console.log(`Execution started: ${execution.id}`);
// Usage - Run with input data
const executionWithData = await executeScenario('12345', {
lead: {
email: 'prospect@example.com',
name: 'John Doe',
company: 'Acme Corp'
}
});
Getting Execution History
Fetch execution logs:
const getExecutionHistory = async (scenarioId, filters = {}) => {
const params = new URLSearchParams({
limit: filters.limit || 50,
from: filters.from,
to: filters.to,
status: filters.status // 'success', 'error', 'running'
});
const response = await makeRequest(`/scenarios/${scenarioId}/executions?${params.toString()}`);
return response;
};
// Usage - Get failed executions from last 24h
const failedExecutions = await getExecutionHistory('12345', {
from: new Date(Date.now() - 86400000).toISOString(),
status: 'error',
limit: 100
});
failedExecutions.data.forEach(exec => {
console.log(`Execution ${exec.id}: ${exec.error?.message}`);
});
Getting Execution Details
Fetch a single execution:
const getExecution = async (executionId) => {
const response = await makeRequest(`/executions/${executionId}`);
return response;
};
// Usage
const execution = await getExecution('98765');
console.log(`Status: ${execution.status}`);
console.log(`Duration: ${execution.duration}ms`);
console.log(`Modules executed: ${execution.modulesExecuted}`);
Stopping Running Execution
Cancel an execution:
const stopExecution = async (executionId) => {
await makeRequest(`/executions/${executionId}`, {
method: 'DELETE'
});
console.log(`Execution ${executionId} stopped`);
};
Webhook Management
Creating Webhook
Set up an incoming webhook:
const createWebhook = async (webhookData) => {
const webhook = {
name: webhookData.name,
scenarioId: webhookData.scenarioId,
type: 'custom', // 'custom' or 'raw'
hookType: 'HEAD', // 'HEAD' or 'GET'
security: {
type: 'none' // 'none', 'basic', 'token'
}
};
const response = await makeRequest('/webhooks', {
method: 'POST',
body: JSON.stringify(webhook)
});
return response;
};
// Usage
const webhook = await createWebhook({
name: 'Lead Capture Webhook',
scenarioId: '12345',
type: 'custom',
hookType: 'HEAD',
security: { type: 'none' }
});
console.log(`Webhook URL: ${hook.url}`);
Listing Webhooks
Fetch all webhooks:
const listWebhooks = async () => {
const response = await makeRequest('/webhooks');
return response;
};
// Usage
const webhooks = await listWebhooks();
webhooks.data.forEach(webhook => {
console.log(`${webhook.name}: ${webhook.url}`);
});
Deleting Webhook
Delete a webhook:
const deleteWebhook = async (webhookId) => {
await makeRequest(`/webhooks/${webhookId}`, {
method: 'DELETE'
});
console.log(`Webhook ${webhookId} deleted`);
};
Team and User Management
Listing Team Members
Fetch users in an organization:
const listTeamMembers = async (organizationId) => {
const response = await makeRequest(`/organizations/${organizationId}/users`);
return response;
};
// Usage
const members = await listTeamMembers('org-123');
members.data.forEach(member => {
console.log(`${member.email} - ${member.role}`);
});
Adding Team Member
Invite a user to the organization:
const addTeamMember = async (organizationId, email, role) => {
const response = await makeRequest(`/organizations/${organizationId}/users`, {
method: 'POST',
body: JSON.stringify({
email: email,
role: role // 'viewer', 'builder', 'manager', 'admin'
})
});
return response;
};
// Usage
await addTeamMember('org-123', 'newuser@example.com', 'builder');
Updating User Role
Change user permissions:
const updateUserRole = async (organizationId, userId, newRole) => {
await makeRequest(`/organizations/${organizationId}/users/${userId}`, {
method: 'PATCH',
body: JSON.stringify({ role: newRole })
});
console.log(`User ${userId} role updated to ${newRole}`);
};
User Roles
| Role | Permissions |
|---|---|
| Viewer | View scenarios, no edits |
| Builder | Create/edit scenarios |
| Manager | Manage team, billing |
| Admin | Full organization access |
Rate Limiting
Understanding Rate Limits
| Plan | Requests/Minute | Burst Limit |
|---|---|---|
| Free | 60 | 100 |
| Core | 120 | 200 |
| Pro | 300 | 500 |
| Teams | 600 | 1000 |
| Enterprise | Custom | Custom |
Rate Limit Headers
| Header | Description |
|---|---|
X-RateLimit-Limit |
Max requests per minute |
X-RateLimit-Remaining |
Remaining requests |
X-RateLimit-Reset |
Seconds until reset |
Implementing Rate Limit Handling
Example for exponential backoff on 429:
const makeRateLimitedRequest = async (endpoint, options = {}, maxRetries = 3) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await makeRequest(endpoint, options);
const remaining = response.headers.get('X-RateLimit-Remaining');
if (remaining < 10) {
console.warn(`Low rate limit: ${remaining} remaining`);
}
return response;
} catch (error) {
if (error.message.includes('429') && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000;
console.log(`Rate limited. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
};
Production Deployment Checklist
Before going live, ensure:
- [ ] Use API keys for internal, OAuth for client integrations
- [ ] Store credentials securely (encrypted database)
- [ ] Implement rate limiting and request queuing
- [ ] Set up execution monitoring and alerting
- [ ] Configure error notifications (email, Slack)
- [ ] Implement retry logic for failed executions
- [ ] Add comprehensive logging
- [ ] Backup/export critical scenarios
Real-World Use Cases
Agency Client Management
A marketing agency automates 100+ client accounts.
Implementation Steps:
- Multi-account OAuth integration
- Bulk scenario deployment via API
- Automated client usage reporting
Outcome: 70% time savings and consistent deployments.
E-commerce Order Processing
An online store automates order fulfillment.
Implementation Steps:
- Shopify webhook triggers Make scenario
- Scenario processes order and updates warehouse system
- Error handling with retry logic
Outcome: No manual entry, 99.9% accuracy.
Conclusion
The Make API offers robust workflow automation. To implement effectively:
- Use API key for internal, OAuth 2.0 for multi-tenant apps
- Manage scenarios, executions, webhooks programmatically
- Use team management for organization control
- Respect rate limits (60-600 requests/min, plan-based)
- Monitor executions in production
- Apidog streamlines API testing and collaboration (learn more)
How do I authenticate with Make API?
Use API key from Developer settings for internal integrations, or OAuth 2.0 for multi-tenant applications.
Can I trigger scenarios programmatically?
Yes. Use the /scenarios/{id}/execute endpoint to run scenarios manually with optional input data.
What are Make rate limits?
Rate limits range from 60 requests/minute (Free) to 600 requests/minute (Teams/Enterprise).
How do I get execution logs?
Use /scenarios/{id}/executions to fetch execution history, filterable by date and status.
Can I create webhooks via API?
Yes. Use /webhooks endpoint to create, list, and delete webhooks for scenarios.
Top comments (0)