Access the Instantly API v2 with managed authentication. Manage cold email campaigns, leads, sending accounts, and view analytics.
# List campaigns
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
https://api.maton.ai/instantly/{native-api-path}
Maton proxies requests to api.instantly.ai and automatically injects your API key.
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"
Manage your Instantly connections at https://api.maton.ai.
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'instantly'}).encode()
req = urllib.request.Request('https://api.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
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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": "{connection_id}",
"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.
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
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://api.maton.ai/instantly/api/v2/campaigns')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '{connection_id}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple connections, always include this header to ensure requests go to the intended account.
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 paginationGET /instantly/api/v2/campaigns/{campaign_id}
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
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 only
- `search` - 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 (not `status`) 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://api.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://api.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 `pending` status 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 -g` when 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
1. Check that the `MATON_API_KEY` environment variable is set:
echo $MATON_API_KEY
2. Verify the API key is valid by listing connections:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
1. Ensure your URL path starts with `instantly`. For example:
- Correct: `https://api.maton.ai/instantly/api/v2/campaigns`
- Incorrect: `https://api.maton.ai/api/v2/campaigns`
## Resources
- [Instantly API V2 Documentation](https://developer.instantly.ai/api/v2)
- [Instantly API Introduction](https://developer.instantly.ai/)
- [Instantly Help Center](https://help.instantly.ai/)
- [Maton Community](https://discord.com/invite/dBfFAcefs2)
- [Maton Support](mailto:support@maton.ai)
共 2 个版本