Instantly
Access the Instantly API v2 with managed authentication. Manage cold email campaigns, leads, sending accounts, and view analytics.
Quick Start
# List campaigns
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/instantly/api/v2/campaigns?limit=10')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Base URL
https://gateway.maton.ai/instantly/{native-api-path}
Replace {native-api-path} with the actual Instantly API endpoint path. The gateway proxies requests to api.instantly.ai and automatically injects your API key.
Authentication
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Getting Your API Key
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Copy your API key
Connection Management
Manage your Instantly connections at https://ctrl.maton.ai.
List Connections
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections?app=instantly&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Create Connection
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'instantly'}).encode()
req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Get Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"connection": {
"connection_id": "e4dca622-b9cf-4ed6-b52e-fa681345f5ac",
"status": "ACTIVE",
"creation_time": "2026-02-11T22:19:35.798712Z",
"last_updated_time": "2026-02-11T22:20:15.702846Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "instantly",
"metadata": {}
}
}
Open the returned url in a browser to complete authorization.
Delete Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Specifying Connection
If you have multiple Instantly connections, specify which one to use with the Maton-Connection header:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/instantly/api/v2/campaigns')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', 'e4dca622-b9cf-4ed6-b52e-fa681345f5ac')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If omitted, the gateway uses the default (oldest) active connection.
API Reference
Campaigns
List Campaigns
GET /instantly/api/v2/campaigns?limit=10&status=1&search=keyword
Query parameters:
limit- Number of results (default: 10)status- Campaign status filter (0=draft, 1=active, 2=paused, 3=completed)search- Search by campaign namestarting_after- Cursor for pagination
Get Campaign
GET /instantly/api/v2/campaigns/{campaign_id}
Create Campaign
POST /instantly/api/v2/campaigns
Content-Type: application/json
{
"name": "My Campaign",
"campaign_schedule": {
"schedules": [
{
"name": "My Schedule",
"timing": {
"from": "09:00",
"to": "17:00"
},
"days": {
"0": true,
"1": true,
"2": true,
"3": true,
"4": true
},
"timezone": "Etc/GMT+5"
}
]
}
}
Note: Timezone must use Etc/GMT format (e.g., "Etc/GMT+5", "Etc/GMT-8", "Etc/GMT+12").
#### Activate Campaign
```bash
POST /instantly/api/v2/campaigns/{campaign_id}/activate
Pause Campaign
POST /instantly/api/v2/campaigns/{campaign_id}/pause
Delete Campaign
DELETE /instantly/api/v2/campaigns/{campaign_id}
Search Campaigns by Lead Email
GET /instantly/api/v2/campaigns/search-by-contact?search=lead@example.com
Leads
Create Lead
POST /instantly/api/v2/leads
Content-Type: application/json
{
"campaign_id": "019bb3bd-9963-789e-b776-6c6927ef3f79",
"email": "lead@example.com",
"first_name": "John",
"last_name": "Doe",
"company_name": "Acme Inc",
"variables": {
"custom_field": "custom_value"
}
}
Bulk Add Leads
POST /instantly/api/v2/leads
Content-Type: application/json
{
"campaign_id": "019bb3bd-9963-789e-b776-6c6927ef3f79",
"leads": [
{
"email": "lead1@example.com",
"first_name": "John"
},
{
"email": "lead2@example.com",
"first_name": "Jane"
}
]
}
List Leads
Note: This is a POST endpoint due to complex filtering requirements.
POST /instantly/api/v2/leads/list
Content-Type: application/json
{
"campaign_id": "019bb3bd-9963-789e-b776-6c6927ef3f79",
"limit": 100
}
Get Lead
GET /instantly/api/v2/leads/{lead_id}
Delete Lead
DELETE /instantly/api/v2/leads/{lead_id}
Move Leads
POST /instantly/api/v2/leads/move
Content-Type: application/json
{
"lead_ids": ["lead_id_1", "lead_id_2"],
"to_campaign_id": "target_campaign_id"
}
Lead Lists
List Lead Lists
GET /instantly/api/v2/lead-lists?limit=10
Create Lead List
POST /instantly/api/v2/lead-lists
Content-Type: application/json
{
"name": "My Lead List"
}
Get Lead List
GET /instantly/api/v2/lead-lists/{list_id}
Update Lead List
PATCH /instantly/api/v2/lead-lists/{list_id}
Content-Type: application/json
{
"name": "Updated List Name"
}
Delete Lead List
DELETE /instantly/api/v2/lead-lists/{list_id}
Accounts (Sending Email Accounts)
List Accounts
GET /instantly/api/v2/accounts?limit=10
Get Account
GET /instantly/api/v2/accounts/{email}
Create Account
POST /instantly/api/v2/accounts
Content-Type: application/json
{
"email": "sender@example.com",
"first_name": "John",
"last_name": "Doe",
"provider_code": "google",
"smtp_host": "smtp.gmail.com",
"smtp_port": 587,
"smtp_username": "sender@example.com",
"smtp_password": "app_password",
"imap_host": "imap.gmail.com",
"imap_port": 993,
"imap_username": "sender@example.com",
"imap_password": "app_password"
}
Update Account
PATCH /instantly/api/v2/accounts/{email}
Content-Type: application/json
{
"first_name": "Jane"
}
Delete Account
DELETE /instantly/api/v2/accounts/{email}
Enable Warmup
POST /instantly/api/v2/accounts/warmup/enable
Content-Type: application/json
{
"emails": ["account1@example.com", "account2@example.com"]
}
Disable Warmup
POST /instantly/api/v2/accounts/warmup/disable
Content-Type: application/json
{
"emails": ["account1@example.com"]
}
Emails (Unibox)
List Emails
GET /instantly/api/v2/emails?limit=20
Get Email
GET /instantly/api/v2/emails/{email_id}
Reply to Email
POST /instantly/api/v2/emails/reply
Content-Type: application/json
{
"reply_to_uuid": "email_uuid",
"body": "Thank you for your response!"
}
Forward Email
POST /instantly/api/v2/emails/forward
Content-Type: application/json
{
"email_uuid": "email_uuid",
"to": "forward@example.com"
}
Mark Thread as Read
POST /instantly/api/v2/emails/threads/{thread_id}/mark-as-read
Get Unread Count
GET /instantly/api/v2/emails/unread/count
Update Email
PATCH /instantly/api/v2/emails/{email_id}
Content-Type: application/json
{
"is_read": true
}
Delete Email
DELETE /instantly/api/v2/emails/{email_id}
Analytics
Get Campaign Analytics
GET /instantly/api/v2/campaigns/analytics?id={campaign_id}
Query parameters:
id- Campaign ID (leave empty for all campaigns)start_date- Filter start date (YYYY-MM-DD)end_date- Filter end date (YYYY-MM-DD)exclude_total_leads_count- Set to true for faster response
Get Campaign Analytics Overview
GET /instantly/api/v2/campaigns/analytics/overview?id={campaign_id}
Get Daily Campaign Analytics
GET /instantly/api/v2/campaigns/analytics/daily?id={campaign_id}
Get Campaign Step Analytics
GET /instantly/api/v2/campaigns/analytics/steps?id={campaign_id}
Get Warmup Analytics
POST /instantly/api/v2/accounts/warmup/analytics
Content-Type: application/json
{
"emails": ["account@example.com"]
}
Block List
List Block List Entries
GET /instantly/api/v2/block-lists-entries?limit=100
Query parameters:
domains_only- Filter to domain entries onlysearch- Search entries
Create Block List Entry
POST /instantly/api/v2/block-lists-entries
Content-Type: application/json
{
"bl_value": "blocked@example.com"
}
Or block a domain:
POST /instantly/api/v2/block-lists-entries
Content-Type: application/json
{
"bl_value": "blockeddomain.com"
}
Delete Block List Entry
DELETE /instantly/api/v2/block-lists-entries/{entry_id}
Email Verification
Verify Email
GET /instantly/api/v2/email-verification/{email}
If verification takes longer than 10 seconds, status will be pending. Poll this endpoint to check status.
Response fields:
verification_status- Use this field (notstatus) to determine verification result
Background Jobs
Get Background Job Status
GET /instantly/api/v2/background-jobs/{job_id}
Query parameters:
data_fields- Comma-separated fields (e.g.,success_count,failed_count,total_to_process)
Workspace
Get Current Workspace
GET /instantly/api/v2/workspaces/current
Custom Tags
Toggle Tag on Resource
POST /instantly/api/v2/custom-tags/toggle-resource
Content-Type: application/json
{
"tag_id": "tag_uuid",
"resource_id": "campaign_or_account_id",
"resource_type": "campaign"
}
Pagination
Instantly uses cursor-based pagination with limit and starting_after:
GET /instantly/api/v2/campaigns?limit=10&starting_after=cursor_value
Response includes pagination info:
{
"items": [...],
"next_starting_after": "cursor_for_next_page"
}
Use next_starting_after value in the next request's starting_after parameter.
Code Examples
JavaScript
const response = await fetch(
'https://gateway.maton.ai/instantly/api/v2/campaigns?limit=10',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const data = await response.json();
Python
import os
import requests
response = requests.get(
'https://gateway.maton.ai/instantly/api/v2/campaigns',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'},
params={'limit': 10}
)
data = response.json()
Notes
- Instantly API v2 uses snake_case for all field names
- Lead custom variables must be string, number, boolean, or null (no objects/arrays)
- The List Leads endpoint is POST (not GET) due to complex filtering requirements
- Campaign status values: 0=draft, 1=active, 2=paused, 3=completed
- Email verification may return
pendingstatus if it takes longer than 10 seconds - Warmup operations return background job IDs - poll the background jobs endpoint for status
- IMPORTANT: When using curl commands, use
curl -gwhen URLs contain brackets to disable glob parsing - IMPORTANT: When piping curl output to
jq, environment variables may not expand correctly. Use Python examples instead.
Error Handling
| Status | Meaning |
|---|---|
| 400 | Missing Instantly connection or invalid request |
| 401 | Invalid or missing Maton API key |
| 403 | Insufficient API key scopes |
| 429 | Rate limited |
| 4xx/5xx | Passthrough error from Instantly API |
Troubleshooting: API Key Issues
- Check that the
MATON_API_KEYenvironment variable is set:
echo $MATON_API_KEY
- Verify the API key is valid by listing connections:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Troubleshooting: Invalid App Name
- Ensure your URL path starts with
instantly. For example:
- Correct:
https://gateway.maton.ai/instantly/api/v2/campaigns - Incorrect:
https://gateway.maton.ai/api/v2/campaigns